From 6b2721d9293d46f48b5407ef933fd7e0b08c3dfd Mon Sep 17 00:00:00 2001 From: Arvind Shyamsundar Date: Sat, 19 Feb 2022 10:54:56 -0800 Subject: [PATCH] Allow user to specify field(s) used to group XEs * Add a method which enumerates the distinct actions and fields contained within a XEL file. This method accepts a parameter defining how many events to sample before returning the list. * Correspondingly, in the GUI, we call the above method, and present the user with the list, so that they can select appropriate item(s) to import data from. * In the GUI, options are provided to add / remove additional items under Actions / Fields groups, in case the sampling missed some items. * Add test for the new method which returns distinct actions / fields from the XEL. --- Engine/StackResolver.cs | 9 +- Engine/SymSrvHelpers.cs | 2 +- Engine/XELHelper.cs | 79 ++++++-------- GUI/FieldSelection.Designer.cs | 96 +++++++++++++++++ GUI/FieldSelection.cs | 53 ++++++++++ GUI/FieldSelection.resx | 1 + GUI/MainForm.Designer.cs | 157 +++------------------------- GUI/MainForm.cs | 47 +++++---- GUI/MultilineInput.Designer.cs | 30 +----- GUI/SQLBuildsForm.Designer.cs | 59 +---------- GUI/SQLCallstackResolver.GUI.csproj | 10 ++ Tests/Tests.cs | 38 +++++-- latestrelease.txt | 2 +- 13 files changed, 278 insertions(+), 305 deletions(-) create mode 100644 GUI/FieldSelection.Designer.cs create mode 100644 GUI/FieldSelection.cs create mode 100644 GUI/FieldSelection.resx diff --git a/Engine/StackResolver.cs b/Engine/StackResolver.cs index b53e7ab..8b22ef7 100644 --- a/Engine/StackResolver.cs +++ b/Engine/StackResolver.cs @@ -13,6 +13,7 @@ namespace Microsoft.SqlServer.Utils.Misc.SQLCallStackResolver { using System.Text; using System.Text.RegularExpressions; using System.Threading; + using System.Threading.Tasks; using System.Xml; public class StackResolver : IDisposable { @@ -37,9 +38,13 @@ public void CancelRunningTasks() { this.cancelRequested = true; } + public Task, List>> GetDistinctXELFieldsAsync(string[] xelFiles, int eventsToSample) { + return XELHelper.GetDistinctXELActionsFieldsAsync(xelFiles, eventsToSample); + } + /// Public method which to help import XEL files - public Tuple ExtractFromXEL(string[] xelFiles, bool bucketize) { - return XELHelper.ExtractFromXEL(this, xelFiles, bucketize); + public async Task> ExtractFromXEL(string[] xelFiles, bool groupEvents, List relevantFields) { + return await XELHelper.ExtractFromXELAsync(this, xelFiles, groupEvents, relevantFields); } /// Convert virtual-address only type frames to their module+offset format diff --git a/Engine/SymSrvHelpers.cs b/Engine/SymSrvHelpers.cs index d546e63..ae7547e 100644 --- a/Engine/SymSrvHelpers.cs +++ b/Engine/SymSrvHelpers.cs @@ -11,7 +11,7 @@ namespace Microsoft.SqlServer.Utils.Misc.SQLCallStackResolver { using System.Text; public static class SymSrvHelpers { - static int processId = Process.GetCurrentProcess().Id; + static readonly int processId = Process.GetCurrentProcess().Id; /// Wrapper around the symsrv.dll functionality to initialize the symbol load handler for this process. private static bool InitSymSrv(string symPath) { diff --git a/Engine/XELHelper.cs b/Engine/XELHelper.cs index 5116320..6a5f8a9 100644 --- a/Engine/XELHelper.cs +++ b/Engine/XELHelper.cs @@ -3,6 +3,7 @@ namespace Microsoft.SqlServer.Utils.Misc.SQLCallStackResolver { using Microsoft.SqlServer.XEvent.XELite; using System; + using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Globalization; @@ -14,70 +15,35 @@ namespace Microsoft.SqlServer.Utils.Misc.SQLCallStackResolver { internal class XELHelper { /// Read a XEL file, consume all callstacks, optionally bucketize them, and in all cases, return the information as equivalent XML - internal static Tuple ExtractFromXEL(StackResolver parent, string[] xelFiles, bool bucketize) { + internal async static Task> ExtractFromXELAsync(StackResolver parent, string[] xelFiles, bool groupEvents, List fieldsToGroupOn) { Contract.Requires(xelFiles != null); parent.cancelRequested = false; - var callstackSlots = new Dictionary(); - var callstackRaw = new Dictionary(); + var callstackSlots = new ConcurrentDictionary(); + var callstackRaw = new ConcurrentDictionary(); var xmlEquivalent = new StringBuilder(); - - // the below feels quite hacky. Unfortunately till such time that we have strong typing in XELite I believe this is unavoidable - var relevantKeyNames = new string[] { "callstack", "call_stack", "stack_frames" }; foreach (var xelFileName in xelFiles) { if (File.Exists(xelFileName)) { parent.StatusMessage = $@"Reading {xelFileName}..."; var xeStream = new XEFileEventStreamer(xelFileName); - xeStream.ReadEventStream( + await xeStream.ReadEventStream( () => { return Task.CompletedTask; }, evt => { - var allStacks = (from actTmp in evt.Actions - from keyName in relevantKeyNames - where actTmp.Key.ToLower(CultureInfo.CurrentCulture).StartsWith(keyName) - select actTmp.Value as string) - .Union( - from fldTmp in evt.Fields - from keyName in relevantKeyNames - where fldTmp.Key.ToLower(CultureInfo.CurrentCulture).StartsWith(keyName) - select fldTmp.Value as string); - - foreach (var callStackString in allStacks) { - if (string.IsNullOrEmpty(callStackString)) { - continue; - } - - if (bucketize) { - lock (callstackSlots) { - if (!callstackSlots.ContainsKey(callStackString)) { - callstackSlots.Add(callStackString, 1); - } - else { - callstackSlots[callStackString]++; - } - } - } - else { - var evtId = string.Format(CultureInfo.CurrentCulture, "File: {0}, Timestamp: {1}, UUID: {2}:", xelFileName, evt.Timestamp.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss.fffffff", CultureInfo.CurrentCulture), evt.UUID); - lock (callstackRaw) { - if (!callstackRaw.ContainsKey(evtId)) { - callstackRaw.Add(evtId, callStackString); - } - else { - callstackRaw[evtId] += $"{Environment.NewLine}{callStackString}"; - } - } - } + var eventKey = string.Join(Environment.NewLine, evt.Actions.Union(evt.Fields).Join(fieldsToGroupOn, l => l.Key, r => r, (l, r) => new { val = l.Value.ToString() }).Select(v => v.val)).Trim(); + if (!string.IsNullOrWhiteSpace(eventKey)) { + if (groupEvents) callstackSlots.AddOrUpdate(eventKey, 1, (k, v) => v + 1); + else callstackRaw.AddOrUpdate(string.Format(CultureInfo.CurrentCulture, "File: {0}, Timestamp: {1}, UUID: {2}:", xelFileName, evt.Timestamp.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss.fffffff", CultureInfo.CurrentCulture), evt.UUID), eventKey, (k, v) => v + $"{Environment.NewLine}{eventKey}"); } return Task.CompletedTask; }, - CancellationToken.None).Wait(); + CancellationToken.None); } } parent.StatusMessage = "Finished reading file(s), finalizing output..."; int finalEventCount; - if (bucketize) { + if (groupEvents) { xmlEquivalent.AppendLine(""); parent.globalCounter = 0; foreach (var item in callstackSlots.OrderByDescending(key => key.Value)) { @@ -117,5 +83,28 @@ where fldTmp.Key.ToLower(CultureInfo.CurrentCulture).StartsWith(keyName) parent.StatusMessage = $@"Finished processing {xelFiles.Length} XEL files"; return new Tuple(finalEventCount, xmlEquivalent.ToString()); } + + internal async static Task, List>> GetDistinctXELActionsFieldsAsync(string[] xelFiles, int eventsToSampleFromEachFile) { + Contract.Requires(xelFiles != null && eventsToSampleFromEachFile > 0); + var allActions = new HashSet(); + var allFields = new HashSet(); + foreach (var file in xelFiles) { + var numEvents = 0; + var cts = new CancellationTokenSource(); + var xeStream = new XEFileEventStreamer(file); + try { + await xeStream.ReadEventStream( + evt => { + if (Interlocked.Increment(ref numEvents) > eventsToSampleFromEachFile) cts.Cancel(); + lock (allActions) evt.Actions.Select(action => allActions.Add(action.Key)).Count(); + lock (allFields) evt.Fields.Select(field => allFields.Add(field.Key)).Count(); + return Task.CompletedTask; + }, cts.Token); + } catch (OperationCanceledException) { + } + } + + return new Tuple, List>(allActions.OrderBy(k => k).ToList(), allFields.OrderBy(k => k).ToList()); + } } } diff --git a/GUI/FieldSelection.Designer.cs b/GUI/FieldSelection.Designer.cs new file mode 100644 index 0000000..7dd25ca --- /dev/null +++ b/GUI/FieldSelection.Designer.cs @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License - see LICENSE file in this repo. +namespace Microsoft.SqlServer.Utils.Misc.SQLCallStackResolver { + partial class FieldSelection { + private System.ComponentModel.IContainer components = null; + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + #region Windows Form Designer generated code + private void InitializeComponent() { + System.Windows.Forms.ColumnHeader DataItem; + this.OKButton = new System.Windows.Forms.Button(); + this.listViewActionsFields = new System.Windows.Forms.ListView(); + this.label2 = new System.Windows.Forms.Label(); + this.AddItemButton = new System.Windows.Forms.Button(); + this.DelItemButton = new System.Windows.Forms.Button(); + DataItem = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.SuspendLayout(); + DataItem.Width = 400; + this.OKButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.OKButton.Location = new System.Drawing.Point(125, 338); + this.OKButton.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.OKButton.Name = "OKButton"; + this.OKButton.Size = new System.Drawing.Size(45, 30); + this.OKButton.TabIndex = 1; + this.OKButton.Text = "OK"; + this.OKButton.UseVisualStyleBackColor = true; + this.OKButton.Click += new System.EventHandler(this.OKButton_Click); + this.listViewActionsFields.AutoArrange = false; + this.listViewActionsFields.CheckBoxes = true; + this.listViewActionsFields.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + DataItem}); + this.listViewActionsFields.FullRowSelect = true; + this.listViewActionsFields.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.listViewActionsFields.HideSelection = false; + this.listViewActionsFields.LabelEdit = true; + this.listViewActionsFields.Location = new System.Drawing.Point(9, 46); + this.listViewActionsFields.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.listViewActionsFields.Name = "listViewActionsFields"; + this.listViewActionsFields.Size = new System.Drawing.Size(278, 288); + this.listViewActionsFields.TabIndex = 0; + this.listViewActionsFields.UseCompatibleStateImageBehavior = false; + this.listViewActionsFields.View = System.Windows.Forms.View.Details; + this.label2.Location = new System.Drawing.Point(10, 8); + this.label2.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(278, 35); + this.label2.TabIndex = 3; + this.label2.Text = "Select the XEvent actions / fields to import (by default, callstack action / fiel" + + "d names are automatically selected):"; + this.AddItemButton.Location = new System.Drawing.Point(209, 338); + this.AddItemButton.Name = "AddItemButton"; + this.AddItemButton.Size = new System.Drawing.Size(38, 23); + this.AddItemButton.TabIndex = 2; + this.AddItemButton.Text = "Add"; + this.AddItemButton.UseVisualStyleBackColor = true; + this.AddItemButton.Click += new System.EventHandler(this.AddItemButton_Click); + this.DelItemButton.Location = new System.Drawing.Point(249, 338); + this.DelItemButton.Name = "DelItemButton"; + this.DelItemButton.Size = new System.Drawing.Size(38, 23); + this.DelItemButton.TabIndex = 3; + this.DelItemButton.Text = "Del"; + this.DelItemButton.UseVisualStyleBackColor = true; + this.DelItemButton.Click += new System.EventHandler(this.DelItemButton_Click); + this.AcceptButton = this.OKButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoScroll = true; + this.ClientSize = new System.Drawing.Size(296, 379); + this.Controls.Add(this.DelItemButton); + this.Controls.Add(this.AddItemButton); + this.Controls.Add(this.label2); + this.Controls.Add(this.listViewActionsFields); + this.Controls.Add(this.OKButton); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "FieldSelection"; + this.ShowIcon = false; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.Text = "Select items"; + this.Load += new System.EventHandler(this.ListSelection_Load); + this.ResumeLayout(false); + } + #endregion + private System.Windows.Forms.Button OKButton; + private System.Windows.Forms.ListView listViewActionsFields; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button AddItemButton; + private System.Windows.Forms.Button DelItemButton; + } +} diff --git a/GUI/FieldSelection.cs b/GUI/FieldSelection.cs new file mode 100644 index 0000000..f634b82 --- /dev/null +++ b/GUI/FieldSelection.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License - see LICENSE file in this repo. +namespace Microsoft.SqlServer.Utils.Misc.SQLCallStackResolver { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Windows.Forms; + public partial class FieldSelection : Form { + public List AllActions = new List(); + public List AllFields = new List(); + public List SelectedEventItems = new List(); + private int newItemOrdinal = 1; + public FieldSelection() { + InitializeComponent(); + } + + private void OKButton_Click(object sender, EventArgs e) { + this.SelectedEventItems = listViewActionsFields.Items.Cast().Where(f => f.Checked).Select(f => f.Text).ToList(); + } + + private void ListSelection_Load(object sender, EventArgs e) { + var grpActions = listViewActionsFields.Groups.Add("Actions", "Actions"); + var grpFields = listViewActionsFields.Groups.Add("Fields", "Fields"); + listViewActionsFields.Items.AddRange(this.AllActions.Select(a => new ListViewItem() { Text = a, Group = grpActions, Checked = IsCallStackName(a) }).ToArray()); + listViewActionsFields.Items.AddRange(this.AllFields.Select(f => new ListViewItem() { Text = f, Group = grpFields, Checked = IsCallStackName(f) }).ToArray()); + listViewActionsFields.Items[0].Selected = true; + } + + private bool IsCallStackName(string a) { + return (new List() { "callstack", "call_stack", "stack_frames" }).Where(v => a.StartsWith(v)).Any(); + } + + private void AddItemButton_Click(object sender, EventArgs e) { + if (listViewActionsFields.SelectedItems.Count > 0) { + var selectedGroup = listViewActionsFields.SelectedItems[0].Group; + listViewActionsFields.SelectedItems.Clear(); + var newItem = listViewActionsFields.Items.Add(new ListViewItem() { Text = $"New item {newItemOrdinal}", Group = selectedGroup }); + newItemOrdinal++; + newItem.Selected = true; + listViewActionsFields.EnsureVisible(newItem.Index); + newItem.BeginEdit(); + } + } + + private void DelItemButton_Click(object sender, EventArgs e) { + if (listViewActionsFields.SelectedItems.Count > 0) { + foreach (ListViewItem item in listViewActionsFields.SelectedItems) { + listViewActionsFields.Items.Remove(item); + } + } + } + } +} diff --git a/GUI/FieldSelection.resx b/GUI/FieldSelection.resx new file mode 100644 index 0000000..c110205 --- /dev/null +++ b/GUI/FieldSelection.resx @@ -0,0 +1 @@ +text/microsoft-resx2.0System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089False \ No newline at end of file diff --git a/GUI/MainForm.Designer.cs b/GUI/MainForm.Designer.cs index 2efcac5..a16c1d4 100644 --- a/GUI/MainForm.Designer.cs +++ b/GUI/MainForm.Designer.cs @@ -4,7 +4,6 @@ namespace Microsoft.SqlServer.Utils.Misc.SQLCallStackResolver { partial class MainForm { private System.ComponentModel.IContainer components = null; - protected override void Dispose(bool disposing) { if (disposing) { _resolver.Dispose(); @@ -15,13 +14,7 @@ protected override void Dispose(bool disposing) { } base.Dispose(disposing); } - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); @@ -37,7 +30,7 @@ private void InitializeComponent() { this.binaryPaths = new System.Windows.Forms.TextBox(); this.label2 = new System.Windows.Forms.Label(); this.groupBox3 = new System.Windows.Forms.GroupBox(); - this.BucketizeXEL = new System.Windows.Forms.CheckBox(); + this.GroupXEvents = new System.Windows.Forms.CheckBox(); this.LoadXELButton = new System.Windows.Forms.Button(); this.groupBox2 = new System.Windows.Forms.GroupBox(); this.cachePDB = new System.Windows.Forms.CheckBox(); @@ -77,22 +70,13 @@ private void InitializeComponent() { this.groupBox1.SuspendLayout(); this.statusStrip1.SuspendLayout(); this.SuspendLayout(); - // - // splitContainer2 - // this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill; this.splitContainer2.FixedPanel = System.Windows.Forms.FixedPanel.Panel2; this.splitContainer2.Location = new System.Drawing.Point(0, 0); this.splitContainer2.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.splitContainer2.Name = "splitContainer2"; this.splitContainer2.Orientation = System.Windows.Forms.Orientation.Horizontal; - // - // splitContainer2.Panel1 - // this.splitContainer2.Panel1.Controls.Add(this.splitContainer1); - // - // splitContainer2.Panel2 - // this.splitContainer2.Panel2.Controls.Add(this.groupBox4); this.splitContainer2.Panel2.Controls.Add(this.groupBox3); this.splitContainer2.Panel2.Controls.Add(this.groupBox2); @@ -101,27 +85,15 @@ private void InitializeComponent() { this.splitContainer2.Size = new System.Drawing.Size(1305, 828); this.splitContainer2.SplitterDistance = 510; this.splitContainer2.TabIndex = 30; - // - // splitContainer1 - // this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; this.splitContainer1.Location = new System.Drawing.Point(0, 0); this.splitContainer1.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.splitContainer1.Name = "splitContainer1"; - // - // splitContainer1.Panel1 - // this.splitContainer1.Panel1.Controls.Add(this.callStackInput); - // - // splitContainer1.Panel2 - // this.splitContainer1.Panel2.Controls.Add(this.finalOutput); this.splitContainer1.Size = new System.Drawing.Size(1305, 510); this.splitContainer1.SplitterDistance = 431; this.splitContainer1.TabIndex = 30; - // - // callStackInput - // this.callStackInput.AllowDrop = true; this.callStackInput.Dock = System.Windows.Forms.DockStyle.Fill; this.callStackInput.Location = new System.Drawing.Point(0, 0); @@ -136,9 +108,6 @@ private void InitializeComponent() { this.callStackInput.WordWrap = false; this.callStackInput.DragDrop += new System.Windows.Forms.DragEventHandler(this.CallStackInput_DragDrop); this.callStackInput.DragEnter += new System.Windows.Forms.DragEventHandler(this.CallStackInput_DragOver); - // - // finalOutput - // this.finalOutput.Dock = System.Windows.Forms.DockStyle.Fill; this.finalOutput.Location = new System.Drawing.Point(0, 0); this.finalOutput.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); @@ -149,9 +118,6 @@ private void InitializeComponent() { this.finalOutput.Size = new System.Drawing.Size(870, 510); this.finalOutput.TabIndex = 8; this.finalOutput.WordWrap = false; - // - // groupBox4 - // this.groupBox4.Controls.Add(this.GetPDBDnldScript); this.groupBox4.Controls.Add(this.DLLrecurse); this.groupBox4.Controls.Add(this.BinaryPathPicker); @@ -165,9 +131,6 @@ private void InitializeComponent() { this.groupBox4.TabIndex = 33; this.groupBox4.TabStop = false; this.groupBox4.Text = "SPECIAL CASES"; - // - // GetPDBDnldScript - // this.GetPDBDnldScript.Location = new System.Drawing.Point(947, 16); this.GetPDBDnldScript.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.GetPDBDnldScript.Name = "GetPDBDnldScript"; @@ -176,9 +139,6 @@ private void InitializeComponent() { this.GetPDBDnldScript.Text = "Generate PDB download script"; this.GetPDBDnldScript.UseVisualStyleBackColor = true; this.GetPDBDnldScript.Click += new System.EventHandler(this.GetPDBDnldScript_Click); - // - // DLLrecurse - // this.DLLrecurse.AutoSize = true; this.DLLrecurse.Location = new System.Drawing.Point(673, 20); this.DLLrecurse.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); @@ -187,9 +147,6 @@ private void InitializeComponent() { this.DLLrecurse.TabIndex = 10; this.DLLrecurse.Text = "Search for DLLs and EXE recursively"; this.DLLrecurse.UseVisualStyleBackColor = true; - // - // BinaryPathPicker - // this.BinaryPathPicker.Location = new System.Drawing.Point(635, 18); this.BinaryPathPicker.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.BinaryPathPicker.Name = "BinaryPathPicker"; @@ -198,17 +155,11 @@ private void InitializeComponent() { this.BinaryPathPicker.Text = "..."; this.BinaryPathPicker.UseVisualStyleBackColor = true; this.BinaryPathPicker.Click += new System.EventHandler(this.BinaryPathPicker_Click); - // - // binaryPaths - // this.binaryPaths.Location = new System.Drawing.Point(261, 18); this.binaryPaths.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.binaryPaths.Name = "binaryPaths"; this.binaryPaths.Size = new System.Drawing.Size(367, 22); this.binaryPaths.TabIndex = 9; - // - // label2 - // this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(7, 21); this.label2.Name = "label2"; @@ -218,10 +169,7 @@ private void InitializeComponent() { this.formToolTip.SetToolTip(this.label2, "Only need to do this if you are dealing with incomplete stacks collected by -T365" + "6 OR if you need to get PowerShell commands to download PDBs for a specific buil" + "d of SQL"); - // - // groupBox3 - // - this.groupBox3.Controls.Add(this.BucketizeXEL); + this.groupBox3.Controls.Add(this.GroupXEvents); this.groupBox3.Controls.Add(this.LoadXELButton); this.groupBox3.Location = new System.Drawing.Point(15, 91); this.groupBox3.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); @@ -232,23 +180,16 @@ private void InitializeComponent() { this.groupBox3.TabStop = false; this.groupBox3.Text = "STEP 1: Either directly paste raw callstack(s) in textbox above, or import XEL fi" + "le(s)"; - // - // BucketizeXEL - // - this.BucketizeXEL.AutoSize = true; - this.BucketizeXEL.Checked = true; - this.BucketizeXEL.CheckState = System.Windows.Forms.CheckState.Checked; - this.BucketizeXEL.Location = new System.Drawing.Point(332, 28); - this.BucketizeXEL.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); - this.BucketizeXEL.Name = "BucketizeXEL"; - this.BucketizeXEL.Size = new System.Drawing.Size(676, 21); - this.BucketizeXEL.TabIndex = 22; - this.BucketizeXEL.Text = "Aggregate similar callstacks from XEL (generally leave checked unless you need in" + - "dividual event data)"; - this.BucketizeXEL.UseVisualStyleBackColor = true; - // - // LoadXELButton - // + this.GroupXEvents.AutoSize = true; + this.GroupXEvents.Checked = true; + this.GroupXEvents.CheckState = System.Windows.Forms.CheckState.Checked; + this.GroupXEvents.Location = new System.Drawing.Point(332, 28); + this.GroupXEvents.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.GroupXEvents.Name = "GroupXEvents"; + this.GroupXEvents.Size = new System.Drawing.Size(676, 21); + this.GroupXEvents.TabIndex = 22; + this.GroupXEvents.Text = "Group events from XEL files (generally leave this checked unless you need individual event data)"; + this.GroupXEvents.UseVisualStyleBackColor = true; this.LoadXELButton.Location = new System.Drawing.Point(11, 18); this.LoadXELButton.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.LoadXELButton.Name = "LoadXELButton"; @@ -257,9 +198,6 @@ private void InitializeComponent() { this.LoadXELButton.Text = "Select XEL files and import callstacks"; this.LoadXELButton.UseVisualStyleBackColor = true; this.LoadXELButton.Click += new System.EventHandler(this.LoadXELButton_Click); - // - // groupBox2 - // this.groupBox2.Controls.Add(this.cachePDB); this.groupBox2.Controls.Add(this.selectSQLPDB); this.groupBox2.Controls.Add(this.PDBPathPicker); @@ -274,9 +212,6 @@ private void InitializeComponent() { this.groupBox2.TabIndex = 31; this.groupBox2.TabStop = false; this.groupBox2.Text = "STEP 2: Either use preset symbol downloads or set custom PDB search paths"; - // - // cachePDB - // this.cachePDB.AutoSize = true; this.cachePDB.Location = new System.Drawing.Point(1076, 27); this.cachePDB.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); @@ -287,9 +222,6 @@ private void InitializeComponent() { this.formToolTip.SetToolTip(this.cachePDB, "This option will copy PDBs from the paths specified to the %TEMP%\\SymCache folder" + ". It is highly recommended to use this if you have a UNC path specified."); this.cachePDB.UseVisualStyleBackColor = true; - // - // selectSQLPDB - // this.selectSQLPDB.Location = new System.Drawing.Point(9, 23); this.selectSQLPDB.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.selectSQLPDB.Name = "selectSQLPDB"; @@ -298,9 +230,6 @@ private void InitializeComponent() { this.selectSQLPDB.Text = "Use public PDBs for a known SQL Server build"; this.selectSQLPDB.UseVisualStyleBackColor = true; this.selectSQLPDB.Click += new System.EventHandler(this.SelectSQLPDB_Click); - // - // PDBPathPicker - // this.PDBPathPicker.Location = new System.Drawing.Point(825, 27); this.PDBPathPicker.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.PDBPathPicker.Name = "PDBPathPicker"; @@ -309,9 +238,6 @@ private void InitializeComponent() { this.PDBPathPicker.Text = "..."; this.PDBPathPicker.UseVisualStyleBackColor = true; this.PDBPathPicker.Click += new System.EventHandler(this.PDBPathPicker_Click); - // - // pdbRecurse - // this.pdbRecurse.AutoSize = true; this.pdbRecurse.Checked = true; this.pdbRecurse.CheckState = System.Windows.Forms.CheckState.Checked; @@ -322,26 +248,17 @@ private void InitializeComponent() { this.pdbRecurse.TabIndex = 28; this.pdbRecurse.Text = "Search for PDBs recursively"; this.pdbRecurse.UseVisualStyleBackColor = true; - // - // pdbPaths - // this.pdbPaths.Location = new System.Drawing.Point(507, 26); this.pdbPaths.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.pdbPaths.Name = "pdbPaths"; this.pdbPaths.Size = new System.Drawing.Size(312, 22); this.pdbPaths.TabIndex = 27; - // - // label1 - // this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(392, 30); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(109, 17); this.label1.TabIndex = 26; this.label1.Text = "Path(s) to PDBs"; - // - // groupBox1 - // this.groupBox1.Controls.Add(this.showInlineFrames); this.groupBox1.Controls.Add(this.outputFilePathPicker); this.groupBox1.Controls.Add(this.outputFilePath); @@ -359,9 +276,6 @@ private void InitializeComponent() { this.groupBox1.TabIndex = 30; this.groupBox1.TabStop = false; this.groupBox1.Text = "STEP 0: Input and output options"; - // - // outputFilePathPicker - // this.outputFilePathPicker.Location = new System.Drawing.Point(1224, 22); this.outputFilePathPicker.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.outputFilePathPicker.Name = "outputFilePathPicker"; @@ -369,18 +283,12 @@ private void InitializeComponent() { this.outputFilePathPicker.TabIndex = 32; this.outputFilePathPicker.Text = "..."; this.outputFilePathPicker.UseVisualStyleBackColor = true; - this.outputFilePathPicker.Click += new System.EventHandler(this.outputFilePathPicker_Click); - // - // outputFilePath - // + this.outputFilePathPicker.Click += new System.EventHandler(this.OutputFilePathPicker_Click); this.outputFilePath.Location = new System.Drawing.Point(833, 21); this.outputFilePath.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.outputFilePath.Name = "outputFilePath"; this.outputFilePath.Size = new System.Drawing.Size(384, 22); this.outputFilePath.TabIndex = 31; - // - // label3 - // this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(616, 25); this.label3.Name = "label3"; @@ -399,9 +307,6 @@ private void InitializeComponent() { this.FramesOnSingleLine.Text = "INPUT: Callstack frames are in a single line"; this.formToolTip.SetToolTip(this.FramesOnSingleLine, "Required if copy-pasting XE callstack from SSMS"); this.FramesOnSingleLine.UseVisualStyleBackColor = true; - // - // IncludeLineNumbers - // this.IncludeLineNumbers.AutoSize = true; this.IncludeLineNumbers.Checked = true; this.IncludeLineNumbers.CheckState = System.Windows.Forms.CheckState.Checked; @@ -412,9 +317,6 @@ private void InitializeComponent() { this.IncludeLineNumbers.TabIndex = 16; this.IncludeLineNumbers.Text = "OUTPUT: Source lines (private PDBs)"; this.IncludeLineNumbers.UseVisualStyleBackColor = true; - // - // EnterBaseAddresses - // this.EnterBaseAddresses.Location = new System.Drawing.Point(411, 21); this.EnterBaseAddresses.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.EnterBaseAddresses.Name = "EnterBaseAddresses"; @@ -424,9 +326,6 @@ private void InitializeComponent() { this.formToolTip.SetToolTip(this.EnterBaseAddresses, "Required for working with XEL files and hex address-only callstacks"); this.EnterBaseAddresses.UseVisualStyleBackColor = true; this.EnterBaseAddresses.Click += new System.EventHandler(this.EnterBaseAddresses_Click); - // - // includeOffsets - // this.includeOffsets.AutoSize = true; this.includeOffsets.Location = new System.Drawing.Point(616, 50); this.includeOffsets.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); @@ -435,9 +334,6 @@ private void InitializeComponent() { this.includeOffsets.TabIndex = 17; this.includeOffsets.Text = "OUTPUT: Func offsets"; this.includeOffsets.UseVisualStyleBackColor = true; - // - // RelookupSource - // this.RelookupSource.AutoSize = true; this.RelookupSource.Location = new System.Drawing.Point(9, 47); this.RelookupSource.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); @@ -446,9 +342,6 @@ private void InitializeComponent() { this.RelookupSource.TabIndex = 16; this.RelookupSource.Text = "INPUT: Re-lookup source (rare case, needs private PDBs)"; this.RelookupSource.UseVisualStyleBackColor = true; - // - // ResolveCallStackButton - // this.ResolveCallStackButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.ResolveCallStackButton.Location = new System.Drawing.Point(15, 230); this.ResolveCallStackButton.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); @@ -458,9 +351,6 @@ private void InitializeComponent() { this.ResolveCallStackButton.Text = "STEP 3: Resolve callstacks!"; this.ResolveCallStackButton.UseVisualStyleBackColor = true; this.ResolveCallStackButton.Click += new System.EventHandler(this.ResolveCallstacks_Click); - // - // statusStrip1 - // this.statusStrip1.ImageScalingSize = new System.Drawing.Size(20, 20); this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.statusLabel, @@ -472,23 +362,14 @@ private void InitializeComponent() { this.statusStrip1.Size = new System.Drawing.Size(1305, 30); this.statusStrip1.TabIndex = 31; this.statusStrip1.Text = "statusStrip1"; - // - // statusLabel - // this.statusLabel.AutoSize = false; this.statusLabel.Name = "statusLabel"; this.statusLabel.Size = new System.Drawing.Size(700, 24); this.statusLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; - // - // progressBar - // this.progressBar.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; this.progressBar.AutoSize = false; this.progressBar.Name = "progressBar"; this.progressBar.Size = new System.Drawing.Size(100, 22); - // - // cancelButton - // this.cancelButton.AutoSize = false; this.cancelButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; this.cancelButton.Enabled = false; @@ -500,10 +381,7 @@ private void InitializeComponent() { this.cancelButton.Text = "Cancel"; this.cancelButton.TextImageRelation = System.Windows.Forms.TextImageRelation.TextBeforeImage; this.cancelButton.Visible = false; - this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click); - // - // showInlineFrames - // + this.cancelButton.Click += new System.EventHandler(this.CancelButton_Click); this.showInlineFrames.AutoSize = true; this.showInlineFrames.Checked = true; this.showInlineFrames.CheckState = System.Windows.Forms.CheckState.Checked; @@ -514,9 +392,6 @@ private void InitializeComponent() { this.showInlineFrames.TabIndex = 33; this.showInlineFrames.Text = "OUTPUT: Inline frames"; this.showInlineFrames.UseVisualStyleBackColor = true; - // - // MainForm - // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoSize = true; @@ -551,9 +426,7 @@ private void InitializeComponent() { this.statusStrip1.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); - } - #endregion private System.Windows.Forms.OpenFileDialog genericOpenFileDlg; private System.Windows.Forms.SplitContainer splitContainer2; @@ -567,7 +440,7 @@ private void InitializeComponent() { private System.Windows.Forms.Button GetPDBDnldScript; private System.Windows.Forms.GroupBox groupBox3; private System.Windows.Forms.CheckBox FramesOnSingleLine; - private System.Windows.Forms.CheckBox BucketizeXEL; + private System.Windows.Forms.CheckBox GroupXEvents; private System.Windows.Forms.Button LoadXELButton; private System.Windows.Forms.Button EnterBaseAddresses; private System.Windows.Forms.GroupBox groupBox2; diff --git a/GUI/MainForm.cs b/GUI/MainForm.cs index 1471304..608e6d6 100644 --- a/GUI/MainForm.cs +++ b/GUI/MainForm.cs @@ -19,9 +19,9 @@ public MainForm() { InitializeComponent(); } - private StackResolver _resolver = new StackResolver(); + private readonly StackResolver _resolver = new StackResolver(); private Task backgroundTask; - private CancellationTokenSource backgroundCTS { get; set; } + private CancellationTokenSource BackgroundCTS { get; set; } private CancellationToken backgroundCT; private string _baseAddressesString = null; @@ -53,7 +53,6 @@ private void ResolveCallstacks_Click(object sender, EventArgs e) { } bool isSingleLineInput = this._resolver.IsInputSingleLine(callStackInput.Text); - if (isSingleLineInput && !FramesOnSingleLine.Checked) { if (DialogResult.Yes == MessageBox.Show(this, "Maybe this is intentional, but your input seems to have all the frames on a single line, but the 'Callstack frames are in single line' checkbox is unchecked. " + @@ -209,7 +208,7 @@ private void ShowStatus(string txt) { Application.DoEvents(); } - private void LoadXELButton_Click(object sender, EventArgs e) { + private async void LoadXELButton_Click(object sender, EventArgs e) { genericOpenFileDlg.Multiselect = true; genericOpenFileDlg.CheckPathExists = true; genericOpenFileDlg.CheckFileExists = true; @@ -218,25 +217,21 @@ private void LoadXELButton_Click(object sender, EventArgs e) { genericOpenFileDlg.Title = "Select XEL file"; var res = genericOpenFileDlg.ShowDialog(this); - if (res != DialogResult.Cancel) { + List relevantXEFields = await GetUserSelectedXEFieldsAsync(); this.ShowStatus("Loading from XEL files; please wait. This may take a while!"); - - this.backgroundTask = Task.Run(() => { - return this._resolver.ExtractFromXEL(genericOpenFileDlg.FileNames, BucketizeXEL.Checked).Item2; + this.backgroundTask = Task.Run(async () => { + return (await this._resolver.ExtractFromXEL(genericOpenFileDlg.FileNames, GroupXEvents.Checked, relevantXEFields)).Item2; }); - this.MonitorBackgroundTask(backgroundTask); - callStackInput.Text = backgroundTask.Result; - this.ShowStatus("Finished importing callstacks from XEL file(s)!"); } } private void MonitorBackgroundTask(Task backgroundTask) { - using (backgroundCTS = new CancellationTokenSource()) { - backgroundCT = backgroundCTS.Token; + using (BackgroundCTS = new CancellationTokenSource()) { + backgroundCT = BackgroundCTS.Token; this.EnableCancelButton(); @@ -261,7 +256,7 @@ private void MonitorBackgroundTask(Task backgroundTask) { } } - private void CallStackInput_DragDrop(object sender, DragEventArgs e) { + private async void CallStackInput_DragDrop(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop, false) == true) { e.Effect = DragDropEffects.All; @@ -273,9 +268,8 @@ private void CallStackInput_DragDrop(object sender, DragEventArgs e) { // if there is any other format in between, it will be rejected by the ExtractFromXEL code if (Path.GetExtension(files[0]).ToLower(CultureInfo.CurrentCulture) == ".xel") { this.ShowStatus("XEL file was dragged; please wait while we extract events from the file"); - - allFilesContent.AppendLine(this._resolver.ExtractFromXEL(files, BucketizeXEL.Checked).Item2); - + List relevantXEFields = await GetUserSelectedXEFieldsAsync(); + allFilesContent.AppendLine((await this._resolver.ExtractFromXEL(files, GroupXEvents.Checked, relevantXEFields)).Item2); this.ShowStatus(string.Empty); } else { // handle the files as text input @@ -289,6 +283,17 @@ private void CallStackInput_DragDrop(object sender, DragEventArgs e) { } } + private async Task> GetUserSelectedXEFieldsAsync() { + using (var fieldsListDialog = new FieldSelection()) { + fieldsListDialog.Text = "Select relevant XEvent fields"; + var xeEventItems = await this._resolver.GetDistinctXELFieldsAsync(genericOpenFileDlg.FileNames, 1000); + fieldsListDialog.AllActions = xeEventItems.Item1; + fieldsListDialog.AllFields = xeEventItems.Item2; + fieldsListDialog.StartPosition = FormStartPosition.CenterParent; + fieldsListDialog.ShowDialog(this); + return fieldsListDialog.SelectedEventItems; + } + } private void CallStackInput_DragOver(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) e.Effect = DragDropEffects.Copy; @@ -346,7 +351,7 @@ private void SelectSQLPDB_Click(object sender, EventArgs e) { } private void MainForm_Load(object sender, EventArgs e) { - DateTime latestReleaseDateTimeServer = DateTime.MinValue; + DateTime latestReleaseDateTimeServer; DateTime latestReleaseDateTimeLocal = DateTime.MinValue; var latestReleaseURLs = ConfigurationManager.AppSettings["LatestReleaseURLs"].Split(';'); // get the timestamp contained within the first valid file within latestReleaseURLs @@ -448,7 +453,7 @@ private void MainForm_Load(object sender, EventArgs e) { } } - private void outputFilePathPicker_Click(object sender, EventArgs e) { + private void OutputFilePathPicker_Click(object sender, EventArgs e) { genericSaveFileDlg.FileName = "resolvedstacks.txt"; genericSaveFileDlg.Filter = "Text files (*.txt)|*.txt"; genericSaveFileDlg.Title = "Save output as"; @@ -460,8 +465,8 @@ private void outputFilePathPicker_Click(object sender, EventArgs e) { } } - private void cancelButton_Click(object sender, EventArgs e) { - this.backgroundCTS.Cancel(); + private void CancelButton_Click(object sender, EventArgs e) { + this.BackgroundCTS.Cancel(); } } } diff --git a/GUI/MultilineInput.Designer.cs b/GUI/MultilineInput.Designer.cs index 2b47e39..dde231e 100644 --- a/GUI/MultilineInput.Designer.cs +++ b/GUI/MultilineInput.Designer.cs @@ -3,20 +3,13 @@ namespace Microsoft.SqlServer.Utils.Misc.SQLCallStackResolver { partial class MultilineInput { private System.ComponentModel.IContainer components = null; - protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MultilineInput)); this.OkButton = new System.Windows.Forms.Button(); @@ -25,9 +18,6 @@ private void InitializeComponent() { this.loadFromFile = new System.Windows.Forms.Button(); this.fileDlg = new System.Windows.Forms.OpenFileDialog(); this.SuspendLayout(); - // - // OkButton - // this.OkButton.DialogResult = System.Windows.Forms.DialogResult.OK; this.OkButton.Location = new System.Drawing.Point(180, 320); this.OkButton.Margin = new System.Windows.Forms.Padding(2); @@ -36,9 +26,6 @@ private void InitializeComponent() { this.OkButton.TabIndex = 0; this.OkButton.Text = "OK"; this.OkButton.UseVisualStyleBackColor = true; - // - // FormCancelButton - // this.FormCancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.FormCancelButton.Location = new System.Drawing.Point(239, 320); this.FormCancelButton.Margin = new System.Windows.Forms.Padding(2); @@ -47,9 +34,6 @@ private void InitializeComponent() { this.FormCancelButton.TabIndex = 1; this.FormCancelButton.Text = "Cancel"; this.FormCancelButton.UseVisualStyleBackColor = true; - // - // InputAddresses - // this.InputAddresses.AllowDrop = true; this.InputAddresses.Location = new System.Drawing.Point(0, 0); this.InputAddresses.Margin = new System.Windows.Forms.Padding(2); @@ -64,9 +48,6 @@ private void InitializeComponent() { this.InputAddresses.DragDrop += new System.Windows.Forms.DragEventHandler(this.InputAddresses_DragDrop); this.InputAddresses.DragOver += new System.Windows.Forms.DragEventHandler(this.InputAddresses_DragOver); this.InputAddresses.KeyDown += new System.Windows.Forms.KeyEventHandler(this.InputAddresses_KeyDown); - // - // loadFromFile - // this.loadFromFile.Location = new System.Drawing.Point(12, 320); this.loadFromFile.Name = "loadFromFile"; this.loadFromFile.Size = new System.Drawing.Size(93, 23); @@ -74,13 +55,7 @@ private void InitializeComponent() { this.loadFromFile.Text = "Load from file"; this.loadFromFile.UseVisualStyleBackColor = true; this.loadFromFile.Click += new System.EventHandler(this.loadFromFile_Click); - // - // fileDlg - // this.fileDlg.Filter = "Text files|*.txt"; - // - // MultilineInput - // this.AcceptButton = this.OkButton; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; @@ -98,15 +73,12 @@ private void InitializeComponent() { this.Text = "MultilineInput"; this.ResumeLayout(false); this.PerformLayout(); - } - #endregion - private System.Windows.Forms.Button OkButton; private System.Windows.Forms.Button FormCancelButton; private System.Windows.Forms.TextBox InputAddresses; private System.Windows.Forms.Button loadFromFile; private System.Windows.Forms.OpenFileDialog fileDlg; } -} \ No newline at end of file +} diff --git a/GUI/SQLBuildsForm.Designer.cs b/GUI/SQLBuildsForm.Designer.cs index 4e969fc..1b79e37 100644 --- a/GUI/SQLBuildsForm.Designer.cs +++ b/GUI/SQLBuildsForm.Designer.cs @@ -3,20 +3,13 @@ namespace Microsoft.SqlServer.Utils.Misc.SQLCallStackResolver { partial class SQLBuildsForm { private System.ComponentModel.IContainer components = null; - protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// private void InitializeComponent() { this.statusStrip1 = new System.Windows.Forms.StatusStrip(); this.downloadStatus = new System.Windows.Forms.ToolStripStatusLabel(); @@ -39,9 +32,6 @@ private void InitializeComponent() { this.splitContainer2.Panel2.SuspendLayout(); this.splitContainer2.SuspendLayout(); this.SuspendLayout(); - // - // statusStrip1 - // this.statusStrip1.ImageScalingSize = new System.Drawing.Size(20, 20); this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.downloadStatus, @@ -52,19 +42,10 @@ private void InitializeComponent() { this.statusStrip1.Size = new System.Drawing.Size(548, 25); this.statusStrip1.TabIndex = 2; this.statusStrip1.Text = "statusStrip1"; - // - // downloadStatus - // this.downloadStatus.Name = "downloadStatus"; this.downloadStatus.Size = new System.Drawing.Size(0, 19); - // - // downloadProgress - // this.downloadProgress.Name = "downloadProgress"; this.downloadProgress.Size = new System.Drawing.Size(100, 17); - // - // splitContainer1 - // this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; this.splitContainer1.FixedPanel = System.Windows.Forms.FixedPanel.Panel2; this.splitContainer1.IsSplitterFixed = true; @@ -72,21 +53,12 @@ private void InitializeComponent() { this.splitContainer1.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.splitContainer1.Name = "splitContainer1"; this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; - // - // splitContainer1.Panel1 - // this.splitContainer1.Panel1.Controls.Add(this.splitContainer2); - // - // splitContainer1.Panel2 - // this.splitContainer1.Panel2.Controls.Add(this.checkPDBAvail); this.splitContainer1.Panel2.Controls.Add(this.dnldButton); this.splitContainer1.Size = new System.Drawing.Size(548, 670); this.splitContainer1.SplitterDistance = 600; this.splitContainer1.TabIndex = 3; - // - // checkPDBAvail - // this.checkPDBAvail.Anchor = System.Windows.Forms.AnchorStyles.None; this.checkPDBAvail.Location = new System.Drawing.Point(88, 7); this.checkPDBAvail.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); @@ -96,9 +68,6 @@ private void InitializeComponent() { this.checkPDBAvail.Text = "Check PDB availability"; this.checkPDBAvail.UseVisualStyleBackColor = true; this.checkPDBAvail.Click += new System.EventHandler(this.CheckPDBAvail_Click); - // - // dnldButton - // this.dnldButton.Anchor = System.Windows.Forms.AnchorStyles.None; this.dnldButton.DialogResult = System.Windows.Forms.DialogResult.OK; this.dnldButton.Location = new System.Drawing.Point(265, 7); @@ -109,56 +78,35 @@ private void InitializeComponent() { this.dnldButton.Text = "Download PDBs"; this.dnldButton.UseVisualStyleBackColor = true; this.dnldButton.Click += new System.EventHandler(this.DownloadPDBs); - // - // splitContainer2 - // this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill; this.splitContainer2.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; this.splitContainer2.IsSplitterFixed = true; this.splitContainer2.Location = new System.Drawing.Point(0, 0); this.splitContainer2.Name = "splitContainer2"; this.splitContainer2.Orientation = System.Windows.Forms.Orientation.Horizontal; - // - // splitContainer2.Panel1 - // this.splitContainer2.Panel1.Controls.Add(this.findNext); this.splitContainer2.Panel1.Controls.Add(this.searchText); this.splitContainer2.Panel1.Controls.Add(this.searchLabel); - // - // splitContainer2.Panel2 - // this.splitContainer2.Panel2.Controls.Add(this.treeviewSyms); this.splitContainer2.Size = new System.Drawing.Size(548, 600); this.splitContainer2.SplitterDistance = 35; this.splitContainer2.TabIndex = 0; - // - // treeviewSyms - // this.treeviewSyms.Dock = System.Windows.Forms.DockStyle.Fill; this.treeviewSyms.Location = new System.Drawing.Point(0, 0); this.treeviewSyms.Margin = new System.Windows.Forms.Padding(4); this.treeviewSyms.Name = "treeviewSyms"; this.treeviewSyms.Size = new System.Drawing.Size(548, 561); this.treeviewSyms.TabIndex = 1; - // - // searchLabel - // this.searchLabel.AutoSize = true; this.searchLabel.Location = new System.Drawing.Point(10, 12); this.searchLabel.Name = "searchLabel"; this.searchLabel.Size = new System.Drawing.Size(150, 17); this.searchLabel.TabIndex = 0; this.searchLabel.Text = "SQL version / keyword"; - // - // searchText - // this.searchText.Location = new System.Drawing.Point(166, 9); this.searchText.Name = "searchText"; this.searchText.Size = new System.Drawing.Size(296, 22); this.searchText.TabIndex = 1; - // - // findNext - // this.findNext.Location = new System.Drawing.Point(468, 9); this.findNext.Name = "findNext"; this.findNext.Size = new System.Drawing.Size(75, 23); @@ -166,9 +114,6 @@ private void InitializeComponent() { this.findNext.Text = "Find"; this.findNext.UseVisualStyleBackColor = true; this.findNext.Click += new System.EventHandler(this.findNext_Click); - // - // SQLBuildsForm - // this.AcceptButton = this.dnldButton; this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; @@ -195,9 +140,7 @@ private void InitializeComponent() { this.splitContainer2.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); - } - #endregion private System.Windows.Forms.StatusStrip statusStrip1; private System.Windows.Forms.ToolStripStatusLabel downloadStatus; @@ -211,4 +154,4 @@ private void InitializeComponent() { private System.Windows.Forms.Label searchLabel; private System.Windows.Forms.Button findNext; } -} \ No newline at end of file +} diff --git a/GUI/SQLCallstackResolver.GUI.csproj b/GUI/SQLCallstackResolver.GUI.csproj index 0af95ca..d1771b6 100644 --- a/GUI/SQLCallstackResolver.GUI.csproj +++ b/GUI/SQLCallstackResolver.GUI.csproj @@ -48,6 +48,12 @@ + + Form + + + FieldSelection.cs + Form @@ -69,6 +75,10 @@ SQLBuildsForm.cs + + FieldSelection.cs + Designer + MainForm.cs Designer diff --git a/Tests/Tests.cs b/Tests/Tests.cs index d9af4c1..3942b1c 100644 --- a/Tests/Tests.cs +++ b/Tests/Tests.cs @@ -229,9 +229,9 @@ public void TestGetSymDetails() { /// Make sure that caching PDB files is working. To do this we must use XEL input to trigger multiple worker threads. [Fact][Trait("Category", "Unit")] - public void SymbolFileCaching() { + public async void SymbolFileCaching() { using (var csr = new StackResolver()) { - var ret = csr.ExtractFromXEL(new[] { @"..\..\..\Tests\TestCases\ImportXEL\xe_wait_completed_0_132353446563350000.xel" }, false); + var ret = await csr.ExtractFromXEL(new[] { @"..\..\..\Tests\TestCases\ImportXEL\xe_wait_completed_0_132353446563350000.xel" }, false, new List(new String[] { "callstack" })); Assert.Equal(550, ret.Item1); var status = csr.ProcessBaseAddresses(File.ReadAllText(@"..\..\..\Tests\TestCases\ImportXEL\xe_wait_base_addresses.txt")); Assert.True(status); @@ -300,9 +300,9 @@ public void RelookupSourceInformation() { /// Validate importing callstack events from XEL files into histogram buckets. [Fact][Trait("Category", "Unit")] - public void ImportBinResolveXELEvents() { + public async void ImportBinResolveXELEvents() { using (var csr = new StackResolver()) { - var ret = csr.ExtractFromXEL(new[] { @"..\..\..\Tests\TestCases\ImportXEL\XESpins_0_131627061603030000.xel" }, true); + var ret = await csr.ExtractFromXEL(new[] { @"..\..\..\Tests\TestCases\ImportXEL\XESpins_0_131627061603030000.xel" }, true, new List(new String[] { "callstack" })); Assert.Equal(4, ret.Item1); var xmldoc = new XmlDocument() { XmlResolver = null }; @@ -361,13 +361,39 @@ public void ImportBinResolveXELEvents() { /// Validate importing individual callstack events from XEL files. [Fact][Trait("Category", "Unit")] - public void ImportIndividualXELEvents() { + public async void ImportIndividualXELEvents() { using (var csr = new StackResolver()) { - var ret = csr.ExtractFromXEL(new[] { @"..\..\..\Tests\TestCases\ImportXEL\xe_wait_completed_0_132353446563350000.xel" }, false); + var ret = await csr.ExtractFromXEL(new[] { @"..\..\..\Tests\TestCases\ImportXEL\xe_wait_completed_0_132353446563350000.xel" }, false, new List(new String[] { "callstack" })); Assert.Equal(550, ret.Item1); } } + [Fact] + [Trait("Category", "Unit")] + public async void XELActionsAndFieldsAsync() { + using (var csr = new StackResolver()) { + var ret = await csr.GetDistinctXELFieldsAsync(new[] { @"..\..\..\Tests\TestCases\ImportXEL\xe_wait_completed_0_132353446563350000.xel" }, 1000); + Assert.Single(ret.Item1); // just the callstack action + Assert.Equal("callstack", ret.Item1.First()); // verify the name + Assert.Equal(5, ret.Item2.Count); // 5 fields + Assert.Equal("duration", ret.Item2.First()); // first field in alphabetical order + Assert.Equal("wait_type", ret.Item2.Last()); // last field in alphabetical order + } + } + + [Fact] + [Trait("Category", "Unit")] + public async void XELActionsAndFieldsAsyncMultipleFiles() { + using (var csr = new StackResolver()) { + var ret = await csr.GetDistinctXELFieldsAsync(new[] { @"..\..\..\Tests\TestCases\ImportXEL\xe_wait_completed_0_132353446563350000.xel", @"..\..\..\Tests\TestCases\ImportXEL\XESpins_0_131627061603030000.xel" }, 1000); + Assert.Single(ret.Item1); // just the callstack action + Assert.Equal("callstack", ret.Item1.First()); // verify the name + Assert.Equal(9, ret.Item2.Count); // 9 fields in total across the 2 XEL files + Assert.Equal("backoffs", ret.Item2.First()); // first field across the 2 XEL files, in alphabetical order + Assert.Equal("worker", ret.Item2.Last()); // last field across the 2 XEL files, in alphabetical order + } + } + /// Validate importing "single-line" callstack (such as when the input is copy-pasted from SSMS). [Fact][Trait("Category", "Unit")] public void SingleLineCallStack() { diff --git a/latestrelease.txt b/latestrelease.txt index d46f554..31ab557 100644 --- a/latestrelease.txt +++ b/latestrelease.txt @@ -1 +1 @@ -2022-02-17 00:00 +2022-02-21 00:00