diff --git a/CtfUnitTest/CtfUnitTest.csproj b/CtfUnitTest/CtfUnitTest.csproj
index 34d2fb3..794a926 100644
--- a/CtfUnitTest/CtfUnitTest.csproj
+++ b/CtfUnitTest/CtfUnitTest.csproj
@@ -7,9 +7,9 @@
     
 
     
-    
-    
-    
+    
+    
+    
   
 
     
diff --git a/DeveloperInfo.md b/DeveloperInfo.md
new file mode 100644
index 0000000..ab36c68
--- /dev/null
+++ b/DeveloperInfo.md
@@ -0,0 +1,51 @@
+# Microsoft Performance Tools Linux / Android - Developer Information
+
+# Prerequisites
+
+See [Readme Dev PreReqs](Readme.md#Dev%20prereqs)
+
+# Code Editing
+
+## Entire Project
+If working on the entire project, or editing .sln or .csproj files 
+[Visual Studio](https://visualstudio.microsoft.com/) is recommended
+
+Open [Microsoft-Perf-Tools-Linux-Android.sln](Microsoft-Perf-Tools-Linux-Android.sln) in VS
+
+## Single Files
+Use your favorite editor
+
+## Build & Test
+
+### Cross Platform Cmd-Line
+- ```dotnet build```
+- ```dotnet test```
+
+### IDE
+- VS Build Solution or Build Project
+
+# Debugging & Testing
+
+## Dev inner loop
+It's often fastest to debug the Unit Test examples since they wrap the plugins. This method keeps runtime overhead to a minimum. See the various *UnitTest projects
+
+- VS Test Explorer is a great way to visualize / run / debug tests. Test -> Test Explorer
+
+## Plugin visualization and trace testing
+- After getting some stabilization in a plugin, it's often fastest to test or investigate multiple traces using a GUI.
+
+- The plugins are not tied to any specific GUI. However the GUI does need to support the [Microsoft Performance Toolkit SDK](https://github.com/microsoft/microsoft-performance-toolkit-sdk)
+
+### WPA GUI
+
+- Using VS2022 Launch Profiles
+  - To Start WPA with your plugin (doesn't auto-open file)
+    - Executable
+        - "C:\PATH\TO\wpa.exe"
+    - Command line arguments - 
+      - -addsearchdir "C:\src\Microsoft-Performance-Tools-Linux-Android\ThePlugin\bin\Debug"
+  - To Start WPA with your plugin AND auto-open file
+    - Executable
+        - "C:\PATH\TO\wpa.exe"
+    - Command line arguments - 
+      - -addsearchdir "C:\src\Microsoft-Performance-Tools-Linux-Android\ThePlugin\bin\Debug" -i "C:\PATH\TO\tracefile.ext"
\ No newline at end of file
diff --git a/DotNetEventPipe/DataOutputTypes/GenericEvent.cs b/DotNetEventPipe/DataOutputTypes/GenericEvent.cs
new file mode 100644
index 0000000..386daaa
--- /dev/null
+++ b/DotNetEventPipe/DataOutputTypes/GenericEvent.cs
@@ -0,0 +1,63 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+using Microsoft.Diagnostics.Tracing;
+using Microsoft.Diagnostics.Tracing.Etlx;
+using Microsoft.Performance.SDK;
+using System;
+using Utilities;
+
+namespace DotNetEventPipe.DataOutputTypes
+{
+    /// 
+    /// A GenericEvent
+    /// 
+    public class GenericEvent
+    {
+        public string EventName { get; }
+        public TraceEventID ID { get; }
+        public TraceEventKeyword Keywords { get; }
+        public TraceEventLevel Level { get; }
+        public TraceEventOpcode Opcode { get; }
+        public string OpcodeName { get; }
+        public string[] PayloadNames { get; }
+        public object[] PayloadValues { get; }
+        public Guid ProviderGuid { get; }
+        public string ProviderName { get; }
+        public int ProcessID { get; }
+        public string ProcessName { get; }
+        public int ProcessorNumber { get; }
+        public int ThreadID { get; }
+        public Timestamp Timestamp { get; }
+        public string[] CallStack { get; }
+        /// 
+        /// filename of the binary / library for the instruction pointer
+        /// 
+        public TraceModuleFile Module { get; }
+        /// 
+        /// Functionname of the instruction pointer
+        /// 
+        public string FullMethodName { get; }
+
+        public GenericEvent(string eventName, TraceEventID id, TraceEventKeyword keywords, TraceEventLevel level, TraceEventOpcode opcode, string opcodeName, string[] payloadNames, object[] payloadValues, Guid providerGuid, string providerName, int processID, string processName, int processorNumber, int threadID, Timestamp timestamp, string[] callStack, TraceModuleFile module, string fullMethodName)
+        {
+            EventName = Common.StringIntern(eventName);
+            ID = id;
+            Keywords = keywords;
+            Level = level;
+            Opcode = opcode;
+            OpcodeName = Common.StringIntern(opcodeName);
+            PayloadNames = payloadNames;
+            PayloadValues = payloadValues;
+            ProviderGuid = providerGuid;
+            ProviderName = providerName;
+            ProcessID = processID;
+            ProcessName = Common.StringIntern(processName);
+            ProcessorNumber = processorNumber;
+            ThreadID = threadID;
+            Timestamp = timestamp;
+            CallStack = callStack;
+            Module = module;
+            FullMethodName = Common.StringIntern(fullMethodName);
+        }
+    }
+}
diff --git a/DotNetEventPipe/DataOutputTypes/ThreadSamplingEvent.cs b/DotNetEventPipe/DataOutputTypes/ThreadSamplingEvent.cs
new file mode 100644
index 0000000..dcd1531
--- /dev/null
+++ b/DotNetEventPipe/DataOutputTypes/ThreadSamplingEvent.cs
@@ -0,0 +1,51 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+using Microsoft.Diagnostics.Tracing.Etlx;
+using Microsoft.Performance.SDK;
+using Utilities;
+
+namespace DotNetEventPipe.DataOutputTypes
+{
+    /// 
+    /// A CPU sampling event that samples at some interval
+    /// Shows process and threads, and stacks that were running on which CPUs at specific times.
+    /// 
+    public readonly struct ThreadSamplingEvent
+    {
+        public int ProcessID { get; }
+        public string ProcessName { get; }
+        public int ProcessorNumber { get; }
+        public int ThreadID { get; }
+        public Timestamp Timestamp { get; }
+        public string[] CallStack { get; }
+        /// 
+        /// filename of the binary / library for the instruction pointer
+        /// 
+        public TraceModuleFile Module { get; }
+        /// 
+        /// Functionname of the instruction pointer
+        /// 
+        public string FullMethodName { get; }
+
+        public ThreadSamplingEvent(
+            int processID,
+            string processName,
+            int processorNumber,
+            int threadID,
+            Timestamp timestamp,
+            string[] callStack,
+            TraceModuleFile module,
+            string fullMethodName
+            )
+        {
+            ProcessID = processID;
+            ProcessName = Common.StringIntern(processName);
+            ProcessorNumber = processorNumber;
+            ThreadID = threadID;
+            Timestamp = timestamp;
+            CallStack = callStack; // Cache whole common stack??
+            Module = module;
+            FullMethodName = Common.StringIntern(fullMethodName);
+        }
+    }
+}
diff --git a/DotNetEventPipe/DataOutputTypes/TraceCallStackProcessed.cs b/DotNetEventPipe/DataOutputTypes/TraceCallStackProcessed.cs
new file mode 100644
index 0000000..745c317
--- /dev/null
+++ b/DotNetEventPipe/DataOutputTypes/TraceCallStackProcessed.cs
@@ -0,0 +1,14 @@
+using Microsoft.Diagnostics.Tracing.Etlx;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace DotnetEventpipe.DataOutputTypes
+{
+    public class TraceCallStackProcessed
+    {
+        public string[] CallStack { get; set; }
+        public TraceModuleFile Module { get; set; }
+        public string FullMethodName { get; set; }
+    }
+}
diff --git a/DotNetEventPipe/DotnetEventpipe.csproj b/DotNetEventPipe/DotnetEventpipe.csproj
new file mode 100644
index 0000000..2110378
--- /dev/null
+++ b/DotNetEventPipe/DotnetEventpipe.csproj
@@ -0,0 +1,52 @@
+
+
+  
+    netstandard2.1
+	1.0.0
+    true
+    Microsoft
+    Microsoft Corp.
+    Performance ToolKit
+    Contains the .NET Trace datasource plugin.
+    Microsoft.Performance.Toolkit.Plugins.DotNetEvent
+    © Microsoft Corporation. All rights reserved.
+    https://github.com/microsoft/Microsoft-Performance-Tools-Linux-Android
+    https://github.com/microsoft/Microsoft-Performance-Tools-Linux-Android
+    LICENSE.txt
+  
+
+  
+    TRACE
+    false
+  
+
+  
+    false
+  
+
+  
+    
+      True
+      
+    
+  
+
+  
+    
+      True
+    
+    
+  
+
+  
+    
+  
+  
+
+  
+
+
+  
+
+
+
diff --git a/DotNetEventPipe/SourceDataCookers/DotnetTraceDataProcessor.cs b/DotNetEventPipe/SourceDataCookers/DotnetTraceDataProcessor.cs
new file mode 100644
index 0000000..0589538
--- /dev/null
+++ b/DotNetEventPipe/SourceDataCookers/DotnetTraceDataProcessor.cs
@@ -0,0 +1,119 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using DotNetEventPipe.Tables;
+using Microsoft.Diagnostics.Tracing;
+using Microsoft.Performance.SDK.Processing;
+using Microsoft.Diagnostics.Tracing.EventPipe;
+using Microsoft.Diagnostics.Tracing.Etlx;
+
+namespace DotNetEventPipe
+{
+    public sealed class DotnetTraceDataProcessor
+        : CustomDataProcessor
+    {
+        private readonly string[] filePaths;
+        private IReadOnlyDictionary fileContent;
+        private DataSourceInfo dataSourceInfo;
+
+        public DotnetTraceDataProcessor(
+           string[] filePaths,
+           ProcessorOptions options,
+           IApplicationEnvironment applicationEnvironment,
+           IProcessorEnvironment processorEnvironment)
+            : base(options, applicationEnvironment, processorEnvironment)
+        {
+            //
+            // Assign the files array to a readonly backing field.
+            //
+
+            this.filePaths = filePaths;
+        }
+
+        public override DataSourceInfo GetDataSourceInfo()
+        {
+            // The DataSourceInfo is used to tell analzyer the time range of the data(if applicable) and any other relevant data for rendering / synchronizing.
+
+            return this.dataSourceInfo;
+
+        }
+
+        protected override Task ProcessAsyncCore(
+           IProgress progress,
+           CancellationToken cancellationToken)
+        {
+            const string ReadPastEndOfStreamExceptionMessage = "Read past end of stream."; // Trace can be partially written but still have data - https://github.com/microsoft/perfview/issues/1637
+            var contentDictionary = new Dictionary();
+
+            foreach (var path in this.filePaths)
+            {
+                var traceStartTime = DateTime.UtcNow.Date;
+
+                var tmpEtlx = Path.Combine(Path.GetTempPath(), Path.GetFileName(path) + ".etlx");
+                var traceEventProcessor = new TraceEventProcessor();
+                try
+                {
+                    string traceLogPath = TraceLog.CreateFromEventPipeDataFile(path, tmpEtlx);
+                    using (TraceLog traceLog = new TraceLog(traceLogPath))
+                    {
+                        TraceLogEventSource source = traceLog.Events.GetSource();
+
+                        contentDictionary[path] = traceEventProcessor;
+                        source.AllEvents += traceEventProcessor.ProcessTraceEvent;
+                        source.Process();
+                        this.dataSourceInfo = new DataSourceInfo(0, source.SessionDuration.Ticks * 100, source.SessionStartTime.ToUniversalTime());
+                    }
+                }
+                catch (Exception e)
+                {
+                    if (e.Message != ReadPastEndOfStreamExceptionMessage || !traceEventProcessor.HasTraceData())
+                    {
+                        throw;
+                    }
+                }
+                finally
+                {
+                    try
+                    {
+                        File.Delete(tmpEtlx);
+                    }
+                    catch (Exception)
+                    {
+                    }
+                }
+            }
+
+            this.fileContent = new ReadOnlyDictionary(contentDictionary);
+
+            return Task.CompletedTask;
+        }
+
+        protected override void BuildTableCore(
+            TableDescriptor tableDescriptor,
+            ITableBuilder tableBuilder)
+        {
+            //
+            // Instantiate the table, and pass the tableBuilder to it.
+            //
+
+            var table = this.InstantiateTable(tableDescriptor.Type);
+            table.Build(tableBuilder);
+        }
+
+        private TraceEventTableBase InstantiateTable(Type tableType)
+        {
+            //
+            // This private method is added to activate the given table type and pass in the file content.
+            //
+
+            var instance = Activator.CreateInstance(tableType, new[] { this.fileContent, });
+            return (TraceEventTableBase)instance;
+        }
+    }
+}
diff --git a/DotNetEventPipe/SourceDataCookers/DotnetTraceProcessingSource.cs b/DotNetEventPipe/SourceDataCookers/DotnetTraceProcessingSource.cs
new file mode 100644
index 0000000..e36da60
--- /dev/null
+++ b/DotNetEventPipe/SourceDataCookers/DotnetTraceProcessingSource.cs
@@ -0,0 +1,73 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Microsoft.Performance.SDK.Processing;
+
+namespace DotNetEventPipe
+{
+    [ProcessingSource(
+        "{890C0A11-011E-43E1-AE28-7E1A903A6633}",   // The GUID must be unique for your Custom Data Source. You can use Visual Studio's Tools -> Create Guid… tool to create a new GUID
+        ".NET (dotnet-trace)",                      // The Custom Data Source MUST have a name
+        @".net trace EventPipe")]                   // The Custom Data Source MUST have a description
+    [FileDataSource(
+        ".nettrace",                                // A file extension is REQUIRED
+        "dotnet-trace")]                            // A description is OPTIONAL. The description is what appears in the file open menu to help users understand what the file type actually is. 
+
+    //
+    // There are two methods to creating a Custom Data Source that is recognized by UI:
+    //    1. Using the helper abstract base classes
+    //    2. Implementing the raw interfaces
+    // This sample demonstrates method 1 where the ProcessingSource abstract class
+    // helps provide a public parameterless constructor and implement the IProcessingSource interface
+    //
+
+    public class DotnetTraceProcessingSource
+        : ProcessingSource
+    {
+        private IApplicationEnvironment applicationEnvironment;
+
+        public override ProcessingSourceInfo GetAboutInfo()
+        {
+            return new ProcessingSourceInfo()
+            {
+                ProjectInfo = new ProjectInfo() { Uri = "https://aka.ms/linuxperftools" },
+            };
+        }
+
+        protected override void SetApplicationEnvironmentCore(IApplicationEnvironment applicationEnvironment)
+        {
+            //
+            // Saves the given application environment into this instance
+            //
+
+            this.applicationEnvironment = applicationEnvironment;
+        }
+
+        protected override bool IsDataSourceSupportedCore(IDataSource dataSource)
+        {
+            return dataSource.IsFile() && Path.GetExtension(dataSource.Uri.LocalPath) == ".nettrace";
+        }
+
+        protected override ICustomDataProcessor CreateProcessorCore(
+            IEnumerable dataSources,
+            IProcessorEnvironment processorEnvironment,
+            ProcessorOptions options)
+        {
+            //
+            // Create a new instance implementing ICustomDataProcessor here to process the specified data sources.
+            // Note that you can have more advanced logic here to create different processors if you would like based on the file, or any other criteria.
+            // You are not restricted to always returning the same type from this method.
+            //
+
+            return new DotnetTraceDataProcessor(
+                dataSources.Select(x => x.Uri.LocalPath).ToArray(),
+                options,
+                this.applicationEnvironment,
+                processorEnvironment);
+        }
+    }
+}
diff --git a/DotNetEventPipe/SourceDataCookers/TraceEventProcessor.cs b/DotNetEventPipe/SourceDataCookers/TraceEventProcessor.cs
new file mode 100644
index 0000000..eb9ecbb
--- /dev/null
+++ b/DotNetEventPipe/SourceDataCookers/TraceEventProcessor.cs
@@ -0,0 +1,108 @@
+using DotnetEventpipe.DataOutputTypes;
+using DotNetEventPipe.DataOutputTypes;
+using Microsoft.Diagnostics.Tracing;
+using Microsoft.Diagnostics.Tracing.Etlx;
+using Microsoft.Diagnostics.Tracing.EventPipe;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Utilities;
+
+namespace DotNetEventPipe
+{
+    public class TraceEventProcessor
+    {
+        const uint MSEC_TO_NS = 1000000;
+        public IReadOnlyList ThreadSamplingEvents => threadSamplingEvents.AsReadOnly();
+        List threadSamplingEvents = new List();
+
+        public IReadOnlyList GenericEvents => genericEvents.AsReadOnly();
+        List genericEvents = new List();
+
+        public bool HasTraceData()
+        {
+            return threadSamplingEvents.Any() || genericEvents.Any();
+        }
+
+        // TODO - Move this to a DataCooker
+        public void ProcessTraceEvent(TraceEvent data)
+        {
+            string eventName = data.ProviderName + "/" + data.EventName;
+
+            TraceCallStack stack = data.CallStack();
+            var stackProcessed = ProcessCallStack(stack);
+
+            switch (data.EventName)
+            {
+                case "Thread/Sample":
+                    var clrTS = (ClrThreadSampleTraceData) data;
+
+
+                    var threadSamplingEvent = new ThreadSamplingEvent(
+                        clrTS.ProcessID,
+                        clrTS.ProcessName,
+                        clrTS.ProcessorNumber,
+                        clrTS.ThreadID,
+                        new Microsoft.Performance.SDK.Timestamp((long)(clrTS.TimeStampRelativeMSec * MSEC_TO_NS)),
+                        stackProcessed?.CallStack,
+                        stackProcessed?.Module,
+                        stackProcessed?.FullMethodName
+                        );
+                    threadSamplingEvents.Add(threadSamplingEvent);
+                    break;
+                default:
+                    var payLoadValues = new object[data.PayloadNames.Length];
+                    for (int i=0; i < data.PayloadNames.Length; i++)
+                    {
+                        payLoadValues[i] = data.PayloadValue(i);
+                    }
+
+                    var genericEvent = new GenericEvent(
+                        data.EventName,
+                        data.ID,
+                        data.Keywords,
+                        data.Level,
+                        data.Opcode,
+                        data.OpcodeName,
+                        data.PayloadNames,
+                        payLoadValues,
+                        data.ProviderGuid,
+                        data.ProviderName,
+                        data.ProcessID,
+                        data.ProcessName,
+                        data.ProcessorNumber,
+                        data.ThreadID,
+                        new Microsoft.Performance.SDK.Timestamp((long)(data.TimeStampRelativeMSec * MSEC_TO_NS)),
+                        stackProcessed?.CallStack,
+                        stackProcessed?.Module,
+                        stackProcessed?.FullMethodName
+                        );
+                    genericEvents.Add(genericEvent);
+                    break;
+            }
+        }
+
+        public TraceCallStackProcessed ProcessCallStack(TraceCallStack stack)
+        {
+            if (stack == null)
+            {
+                return null;
+            }
+
+            var traceCallStackProcessed = new TraceCallStackProcessed();
+            traceCallStackProcessed.Module = stack.CodeAddress.ModuleFile;
+            traceCallStackProcessed.FullMethodName = stack.CodeAddress.FullMethodName;
+            traceCallStackProcessed.CallStack = new string[stack.Depth];
+
+            TraceCallStack current = stack;
+            while (current != null)
+            {
+                traceCallStackProcessed.CallStack[current.Depth - 1] = Common.StringIntern($"{current.CodeAddress.ModuleName}!{current.CodeAddress.FullMethodName}");
+                current = current.Caller;
+            }
+
+            return traceCallStackProcessed;
+        }
+    }
+}
diff --git a/DotNetEventPipe/Tables/CpuSamplingTable.cs b/DotNetEventPipe/Tables/CpuSamplingTable.cs
new file mode 100644
index 0000000..b5f2700
--- /dev/null
+++ b/DotNetEventPipe/Tables/CpuSamplingTable.cs
@@ -0,0 +1,282 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using DotNetEventPipe.DataOutputTypes;
+using Microsoft.Performance.SDK;
+using Microsoft.Performance.SDK.Processing;
+using Utilities.AccessProviders;
+using Utilities.Generators;
+using static Utilities.TimeHelper;
+
+namespace DotNetEventPipe.Tables
+{
+    //
+    // Have the MetadataTable inherit the TableBase class
+    //
+    [Table]              // A category is optional. It useful for grouping different types of tables
+    public sealed class CpuSamplingTable
+        : TraceEventTableBase
+    {
+        public static readonly TableDescriptor TableDescriptor = new TableDescriptor(
+            Guid.Parse("{29C3ECF1-0857-4A3D-B6E3-2197CE1E9C81}"),
+            "CPU Sampling",
+            "Thread Profiler Samples",
+            category: ".NET trace (dotnet-trace)");
+
+        public CpuSamplingTable(IReadOnlyDictionary traceEventProcessor)
+            : base(traceEventProcessor)
+        {
+        }
+
+        private static readonly ColumnConfiguration timestampColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{8FFAE86A-5608-42CB-80E4-01D982AFBCA2}"), "Timestamp", "The timestamp of the sample"),
+            new UIHints { Width = 80 });
+
+        private static readonly ColumnConfiguration functionColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{4E6F6543-925A-479D-947C-CC3E932DA139}"), "Function", "The function of the Instruction Pointer(IP)"),
+            new UIHints { Width = 130 });
+
+        private static readonly ColumnConfiguration moduleColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{A3EE00E4-71A9-47F9-B0B8-448CAEE92BB0}"), "Module", "The module of the Instruction Pointer(IP)"),
+            new UIHints { Width = 130 });
+
+        private static readonly ColumnConfiguration countColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{711ECA36-9B10-4E88-829D-F5E6749E22A6}"), "Count", "The count of samples"),
+            new UIHints { 
+                Width = 130, 
+                AggregationMode = AggregationMode.Sum, // Sum needed instead of Count for flame
+            }); 
+
+        private static readonly ColumnConfiguration callStackColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{5072587D-B69C-4B4B-AEB9-AD51C3FFEBFB}"), "Callstack"),
+                new UIHints { Width = 800, });
+
+        private static readonly ColumnConfiguration threadIdColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{11C8D2B4-3E39-46A4-B9AF-33D2F4E79148}"), "ThreadId"),
+                new UIHints { Width = 80, });
+
+        private static readonly ColumnConfiguration processColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{DA2F0BA6-5F54-449E-A3B8-586DB3D38DCC}"), "Process"),
+                new UIHints { Width = 80, });
+
+        private static readonly ColumnConfiguration processIdColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{357841CB-AE3B-4BFF-A778-C0B6AD3E4CD0}"), "Process Id"),
+                new UIHints { Width = 80, });
+
+        private static readonly ColumnConfiguration cpuColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{DB744571-72CE-4C56-8277-58A402682016}"), "CPU"),
+                new UIHints { Width = 80, });
+
+        private static readonly ColumnConfiguration weightColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{46478B6A-C6FB-4552-AF78-E129E660189B}"), "Sample Weight"),
+                new UIHints { Width = 80, CellFormat = TimestampFormatter.FormatMillisecondsGrouped, });
+
+        private static readonly ColumnConfiguration weightPctColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{EED346D1-98C4-4702-838F-FF3EFF822EC1}"), "Weight %"),
+                new UIHints
+                {
+                    Width = 80,
+                    AggregationMode = AggregationMode.Sum,
+                    SortPriority = 0,
+                    SortOrder = SortOrder.Descending,
+                });
+
+        private static readonly ColumnConfiguration viewportClippedStartTimeCol =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{D0F5DE08-E4FF-45D2-8644-E6D2AACFB2CD}"), "Clipped Start Time"),
+                new UIHints { Width = 80, });
+
+        private static readonly ColumnConfiguration viewportClippedEndTimeCol =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{8AC0B8A4-CE20-4B54-A031-1558B2A9238C}"), "Clipped End Time"),
+                new UIHints { Width = 80, });
+
+        private static readonly ColumnConfiguration clippedWeightCol =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{E5A38163-3FB1-47DC-9E0E-E296041A563D}"), "Clipped Weight"),
+                new UIHints { Width = 80, });
+
+        private IReadOnlyList ThreadSamplingEvents;
+
+        public override void Build(ITableBuilder tableBuilder)
+        {
+            if (TraceEventProcessor == null || TraceEventProcessor.Count == 0)
+            {
+                return;
+            }
+
+            var firstTraceProcessorEventsParsed = TraceEventProcessor.First().Value;  // First Log
+            ThreadSamplingEvents = firstTraceProcessorEventsParsed.ThreadSamplingEvents;
+
+            var tableGenerator = tableBuilder.SetRowCount(ThreadSamplingEvents.Count);
+            var baseProjection = Projection.Index(ThreadSamplingEvents);
+
+            
+            tableGenerator.AddColumn(countColumn, baseProjection.Compose(x => 1));                  // 1 sample
+            tableGenerator.AddColumn(processIdColumn, baseProjection.Compose(x => x.ProcessID));
+            tableGenerator.AddColumn(processColumn, baseProjection.Compose(x => x.ProcessName));
+            tableGenerator.AddColumn(cpuColumn, baseProjection.Compose(x => x.ProcessorNumber));
+            tableGenerator.AddColumn(threadIdColumn, baseProjection.Compose(x => x.ThreadID));
+            tableGenerator.AddColumn(moduleColumn, baseProjection.Compose(x => x.Module?.Name));
+            tableGenerator.AddColumn(functionColumn, baseProjection.Compose(x => x.FullMethodName));
+            tableGenerator.AddHierarchicalColumn(callStackColumn, baseProjection.Compose(x => x.CallStack), new ArrayAccessProvider());
+            var timeStampProjection = Projection.CreateUsingFuncAdaptor((i) => ThreadSamplingEvents[i].Timestamp);
+
+            // Calculating sample weights
+            var tenMsSample = new TimestampDelta(10000000); // 10ms - https://docs.microsoft.com/en-us/dotnet/core/diagnostics/well-known-event-providers
+            var weightProj = Projection.Constant(tenMsSample);
+            var oneNs = new TimestampDelta(1);
+
+            var timeStampStartProjection = baseProjection.Compose(x => x.Timestamp - oneNs); // We will say sample lasted 1ns
+            IProjection viewportClippedStartTimeProj = Projection.ClipTimeToVisibleDomain.Create(timeStampStartProjection);
+            IProjection viewportClippedEndTimeProj = Projection.ClipTimeToVisibleDomain.Create(timeStampProjection);
+
+            IProjection clippedWeightProj = Projection.Select(
+                viewportClippedEndTimeProj,
+                viewportClippedStartTimeProj,
+                new ReduceTimeSinceLastDiff());
+
+            IProjection weightPercentProj = Projection.VisibleDomainRelativePercent.Create(clippedWeightProj);
+
+            IProjection countProj = SequentialGenerator.Create(
+                ThreadSamplingEvents.Count,
+                Projection.Constant(1),
+                Projection.Constant(0));
+
+            tableGenerator.AddColumn(weightColumn, weightProj);
+            tableGenerator.AddColumn(weightPctColumn, weightPercentProj);
+            tableGenerator.AddColumn(timestampColumn, timeStampStartProjection);
+            tableGenerator.AddColumn(viewportClippedStartTimeCol, viewportClippedStartTimeProj);
+            tableGenerator.AddColumn(viewportClippedEndTimeCol, viewportClippedEndTimeProj);
+            tableGenerator.AddColumn(clippedWeightCol, clippedWeightProj);
+
+            var utilByCpuStackConfig = new TableConfiguration("Utilization by CPU, Stack")
+            {
+                Columns = new[]
+              {
+                    cpuColumn,
+                    callStackColumn,
+                    TableConfiguration.PivotColumn,
+                    processColumn,
+                    threadIdColumn,
+                    functionColumn,
+                    moduleColumn,
+                    timestampColumn,
+                    countColumn,
+                    TableConfiguration.GraphColumn,
+                    weightPctColumn
+
+                },
+            };
+            utilByCpuStackConfig.AddColumnRole(ColumnRole.EndTime, timestampColumn);
+            utilByCpuStackConfig.AddColumnRole(ColumnRole.ResourceId, cpuColumn);
+            utilByCpuStackConfig.AddColumnRole(ColumnRole.Duration, weightColumn);
+
+            var utilByCpuConfig = new TableConfiguration("Utilization by CPU")
+            {
+                Columns = new[]
+              {
+                    cpuColumn,
+                    TableConfiguration.PivotColumn,
+                    processColumn,
+                    threadIdColumn,
+                    functionColumn,
+                    moduleColumn,
+                    timestampColumn,
+                    countColumn,
+                    TableConfiguration.GraphColumn,
+                    weightPctColumn
+
+                },
+            };
+            utilByCpuConfig.AddColumnRole(ColumnRole.EndTime, timestampColumn);
+            utilByCpuConfig.AddColumnRole(ColumnRole.ResourceId, cpuColumn);
+            utilByCpuConfig.AddColumnRole(ColumnRole.Duration, weightColumn);
+
+            var utilByProcessConfig = new TableConfiguration("Utilization by Process")
+            {
+                Columns = new[]
+              {
+                    processColumn,
+                    TableConfiguration.PivotColumn,
+                    cpuColumn,
+                    threadIdColumn,
+                    functionColumn,
+                    moduleColumn,
+                    timestampColumn,
+                    countColumn,
+                    TableConfiguration.GraphColumn,
+                    weightPctColumn
+
+                },
+            };
+            utilByProcessConfig.AddColumnRole(ColumnRole.EndTime, timestampColumn);
+            utilByProcessConfig.AddColumnRole(ColumnRole.ResourceId, cpuColumn);
+            utilByProcessConfig.AddColumnRole(ColumnRole.Duration, weightColumn);
+
+            var utilByProcessStackConfig = new TableConfiguration("Utilization by Process, Stack")
+            {
+                Columns = new[]
+              {
+                    processColumn,
+                    callStackColumn,
+                    TableConfiguration.PivotColumn,
+                    cpuColumn,
+                    threadIdColumn,
+                    functionColumn,
+                    moduleColumn,
+                    timestampColumn,
+                    countColumn,
+                    TableConfiguration.GraphColumn,
+                    weightPctColumn
+
+                },
+            };
+            utilByProcessStackConfig.AddColumnRole(ColumnRole.EndTime, timestampColumn);
+            utilByProcessStackConfig.AddColumnRole(ColumnRole.ResourceId, cpuColumn);
+            utilByProcessStackConfig.AddColumnRole(ColumnRole.Duration, weightColumn);
+
+            var flameByProcessStackConfig = new TableConfiguration("Flame by Process, Stack")
+            {
+                Columns = new[]
+              {
+                    processColumn,
+                    callStackColumn,
+                    TableConfiguration.PivotColumn,
+                    cpuColumn,
+                    threadIdColumn,
+                    functionColumn,
+                    moduleColumn,
+                    timestampColumn,
+                    countColumn,
+                    TableConfiguration.GraphColumn,
+                    weightPctColumn
+
+                },
+                ChartType = ChartType.Flame,
+
+            };
+            flameByProcessStackConfig.AddColumnRole(ColumnRole.EndTime, timestampColumn);
+            flameByProcessStackConfig.AddColumnRole(ColumnRole.ResourceId, cpuColumn);
+            flameByProcessStackConfig.AddColumnRole(ColumnRole.Duration, weightColumn);
+
+            var table = tableBuilder
+            .AddTableConfiguration(utilByCpuStackConfig)
+                .SetDefaultTableConfiguration(utilByProcessStackConfig)
+                .AddTableConfiguration(utilByCpuConfig)
+                .AddTableConfiguration(utilByProcessConfig)
+                .AddTableConfiguration(utilByProcessStackConfig)
+                .AddTableConfiguration(flameByProcessStackConfig);
+        }
+    }
+}
diff --git a/DotNetEventPipe/Tables/EventStat.cs b/DotNetEventPipe/Tables/EventStat.cs
new file mode 100644
index 0000000..fe2d5ac
--- /dev/null
+++ b/DotNetEventPipe/Tables/EventStat.cs
@@ -0,0 +1,17 @@
+using Microsoft.Performance.SDK;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace DotNetEventPipe.Tables
+{
+    internal class EventStat
+    {
+        public string ProviderName { get; set; }
+        public string EventName { get; set; }
+        public int Count { get; set; }
+        public int StackCount { get; set; }
+        public Timestamp StartTime { get; set; }
+        public Timestamp EndTime { get; set; }
+    }
+}
diff --git a/DotNetEventPipe/Tables/EventStats.cs b/DotNetEventPipe/Tables/EventStats.cs
new file mode 100644
index 0000000..9abf06c
--- /dev/null
+++ b/DotNetEventPipe/Tables/EventStats.cs
@@ -0,0 +1,127 @@
+using Microsoft.Performance.SDK;
+using Microsoft.Performance.SDK.Extensibility;
+using Microsoft.Performance.SDK.Processing;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace DotNetEventPipe.Tables
+{
+    [Table]
+    public sealed class EventStats : TraceEventTableBase
+    {
+        public static TableDescriptor TableDescriptor = new TableDescriptor(
+           Guid.Parse("{3B038894-8539-47CE-8FB7-8E01226E9DDD}"),
+           "Event Stats",
+           "Event Stats",
+           category: ".NET trace (dotnet-trace)",
+           defaultLayout: TableLayoutStyle.GraphAndTable);
+
+        private static readonly ColumnConfiguration providerNameColumnConfig =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{380F9262-5F13-486D-96B5-3ADB9779E54D}"), "Provider Name"),
+                new UIHints { Width = 300 });
+
+        private static readonly ColumnConfiguration eventNameColumnConfig =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{67BB853A-94FC-49EA-ACB1-E6236D3125F2}"), "Event Name"),
+                new UIHints { Width = 600 });
+
+        private static readonly ColumnConfiguration eventCountColumnConfig =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{C657FBD3-C424-4896-A8DF-B84199499A02}"), "Event Count"),
+                new UIHints { Width = 150, SortOrder = SortOrder.Descending, SortPriority = 0, AggregationMode = AggregationMode.Sum });
+
+        private static readonly ColumnConfiguration stackCountColumnConfig =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{1F98EDA4-A80A-49E6-AB0A-D3323A6E7DD0}"), "Stack Count"),
+                new UIHints { Width = 150, SortOrder = SortOrder.Descending, SortPriority = 1 });
+
+        private static readonly ColumnConfiguration startTimeConfig =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{F4E2F334-AE8E-4B15-88DC-3304D0E3BA91}"), "StartTime"),
+                new UIHints { Width = 150, IsVisible = false });
+
+        private static readonly ColumnConfiguration endTimeConfig =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{191CCB3E-F0A1-40C7-96BA-015E9F99BA17}"), "EndTime"),
+                new UIHints { Width = 150, IsVisible = false });
+
+        public EventStats(IReadOnlyDictionary traceEventProcessor)
+            : base(traceEventProcessor)
+        {
+        }
+
+        public override void Build(ITableBuilder tableBuilder)
+        {
+            if (TraceEventProcessor == null || TraceEventProcessor.Count == 0)
+            {
+                return;
+            }
+
+            var firstTraceProcessorEventsParsed = TraceEventProcessor.First().Value;  // First Log
+            var threadSamplingEvents = firstTraceProcessorEventsParsed.ThreadSamplingEvents;
+            EventStat[] threadSamplingStats = { new EventStat
+            {
+                ProviderName = "Microsoft-DotNETCore-SampleProfiler",
+                EventName = "Thread/Sample",
+                Count = threadSamplingEvents.Count(),
+                StackCount = threadSamplingEvents.Count(f => f.CallStack != null),
+                StartTime = threadSamplingEvents.Min(f => f.Timestamp),
+                EndTime = threadSamplingEvents.Max(f => f.Timestamp),
+            } };
+
+            var genericEvents = firstTraceProcessorEventsParsed.GenericEvents;
+            var genericEventStats = genericEvents.GroupBy(
+                                                    ge => new { ge.ProviderName, ge.EventName },
+                                                    ge => ge,
+                                                    (geGroup, geElems) => new EventStat
+                                                    { 
+                                                        ProviderName = geGroup.ProviderName,
+                                                        EventName = geGroup.EventName,
+                                                        Count = geElems.Count(),
+                                                        StackCount = geElems.Count(f => f.CallStack != null),
+                                                        StartTime = geElems.Min(f => f.Timestamp),
+                                                        EndTime = geElems.Max(f => f.Timestamp),
+                                                    });
+
+            var allEventStats = threadSamplingStats.Union(genericEventStats).ToArray();
+
+            var table = tableBuilder.SetRowCount(allEventStats.Length);
+            var baseProj = Projection.Index(allEventStats);
+
+            var providerNameProj = baseProj.Compose(x => x.ProviderName);
+            var eventNameProj = baseProj.Compose(x => x.EventName);
+            var countProj = baseProj.Compose(x => x.Count);
+            var stackCountProj = baseProj.Compose(x => x.StackCount);
+            var startTimeProj = baseProj.Compose(x => x.StartTime);
+            var endTimeProj = baseProj.Compose(x => x.EndTime);
+
+            table.AddColumn(providerNameColumnConfig, providerNameProj);
+            table.AddColumn(eventNameColumnConfig, eventNameProj);
+            table.AddColumn(eventCountColumnConfig, countProj);
+            table.AddColumn(stackCountColumnConfig, stackCountProj);
+            table.AddColumn(startTimeConfig, startTimeProj);
+            table.AddColumn(endTimeConfig, endTimeProj);
+
+            var tableConfig = new TableConfiguration("Stats")
+            {
+                Columns = new[]
+                {
+                    providerNameColumnConfig,
+                    eventNameColumnConfig,
+                    TableConfiguration.PivotColumn,
+                    stackCountColumnConfig,
+                    TableConfiguration.GraphColumn,
+                    eventCountColumnConfig,
+                }
+            };
+            tableConfig.AddColumnRole(ColumnRole.StartTime, startTimeConfig);
+            tableConfig.AddColumnRole(ColumnRole.EndTime, endTimeConfig);
+            tableConfig.ChartType = ChartType.StackedBars;
+
+            tableBuilder.SetDefaultTableConfiguration(tableConfig);
+        }
+
+    }
+}
diff --git a/DotNetEventPipe/Tables/ExceptionTable.cs b/DotNetEventPipe/Tables/ExceptionTable.cs
new file mode 100644
index 0000000..73e3e0f
--- /dev/null
+++ b/DotNetEventPipe/Tables/ExceptionTable.cs
@@ -0,0 +1,159 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using Microsoft.Diagnostics.Tracing.Parsers.Clr;
+using Microsoft.Performance.SDK.Processing;
+using Utilities;
+using Utilities.AccessProviders;
+using static Utilities.TimeHelper;
+
+namespace DotNetEventPipe.Tables
+{
+    //
+    // Have the MetadataTable inherit the TableBase class
+    //
+    [Table]              // A category is optional. It useful for grouping different types of tables
+    public sealed class ExceptionTable
+        : TraceEventTableBase
+    {
+        public static readonly TableDescriptor TableDescriptor = new TableDescriptor(
+            Guid.Parse("{4975D02A-9950-49B6-847F-C9C0D41DDCBE}"),
+            "Exceptions",
+            "Exceptions",
+            category: ".NET trace (dotnet-trace)");
+
+        public ExceptionTable(IReadOnlyDictionary traceEventProcessor)
+            : base(traceEventProcessor)
+        {
+        }
+
+        private static readonly ColumnConfiguration exceptionTypeColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{E7CD518A-6644-4E17-9AD8-5A034C3F63A2}"), "ExceptionType", "Exception Type"),
+            new UIHints { Width = 305 });
+
+        private static readonly ColumnConfiguration exceptionMessageColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{6A2381EF-6C6F-427F-846F-84D21C7F7793}"), "ExceptionMessage", "Exception Message"),
+            new UIHints { Width = 160 });
+
+        private static readonly ColumnConfiguration exceptionEIP = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{9C107E94-1DB5-400F-B36B-0864BA1B9F94}"), "ExceptionEIP", "Exception EIP"),
+            new UIHints { Width = 125, CellFormat = ColumnFormats.HexFormat, IsVisible = false });
+
+        private static readonly ColumnConfiguration exceptionHRESULTColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{996C5372-9F87-46A5-9D0D-678DCF37D040}"), "ExceptionHRESULT", "Exception HRESULT"),
+            new UIHints { Width = 125, CellFormat = ColumnFormats.HexFormat });
+
+        private static readonly ColumnConfiguration exceptionFlagsColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{5A263CF7-2338-4BC4-9523-FD3EAB8B5AA0}"), "ExceptionFlags", "Exception Flags"),
+            new UIHints { Width = 120 });
+
+        private static readonly ColumnConfiguration clrInstanceIDColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{08C5C067-C27A-4542-A761-B1350B0E8DF4}"), "ClrInstanceID", "Clr Instance ID"),
+            new UIHints { Width = 90, IsVisible = false });
+
+        private static readonly ColumnConfiguration timestampColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{6056D148-77BC-430A-927D-CF7192C02E45}"), "Timestamp", "The timestamp of the exception"),
+            new UIHints { Width = 80 });
+
+
+        private static readonly ColumnConfiguration countColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{09CCE4A5-A3B0-4C25-B05E-609620702FD7}"), "Count", "The count of samples"),
+            new UIHints { 
+                Width = 130, 
+                AggregationMode = AggregationMode.Sum,
+                SortOrder = SortOrder.Descending,
+                SortPriority = 0,
+            });
+
+        private static readonly ColumnConfiguration hascallStackColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{E2DFB281-9904-4595-AB00-26FD104CAB0D}"), "HasCallstack", "Has Callstack"),
+                new UIHints { Width = 40, });
+
+        private static readonly ColumnConfiguration callStackColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{1C9357D2-CA16-439F-A3BE-9E7C5B903CEB}"), "ExStack", "Callstack of the exception"),
+                new UIHints { Width = 430, });
+
+        private static readonly ColumnConfiguration threadIdColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{0D6A5367-BF52-49CD-859E-CAC7F7F5C912}"), "ThreadId"),
+                new UIHints { Width = 80, });
+
+        private static readonly ColumnConfiguration processColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{BE450391-3E06-45C9-ABCB-6786A6CBAB00}"), "Process"),
+                new UIHints { Width = 80, });
+
+        private static readonly ColumnConfiguration processIdColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{E3D33FD4-4120-4EFB-B2B9-16523332B2F5}"), "Process Id"),
+                new UIHints { Width = 80, });
+
+        private static readonly ColumnConfiguration cpuColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{23FA8CD4-C253-426A-BADB-1AAE246A7169}"), "CPU"),
+                new UIHints { Width = 80, });
+
+
+        public override void Build(ITableBuilder tableBuilder)
+        {
+            if (TraceEventProcessor == null || TraceEventProcessor.Count == 0)
+            {
+                return;
+            }
+
+            var firstTraceProcessorEventsParsed = TraceEventProcessor.First().Value;  // First Log
+            var exceptions = firstTraceProcessorEventsParsed.GenericEvents.Where(f => f.ProviderName == "Microsoft-Windows-DotNETRuntime" && f.EventName == "Exception/Start").ToArray();
+
+            var tableGenerator = tableBuilder.SetRowCount(exceptions.Length);
+            var baseProjection = Projection.Index(exceptions);
+
+            tableGenerator.AddColumn(countColumn, baseProjection.Compose(x => 1));                  // 1 sample
+            tableGenerator.AddColumn(exceptionTypeColumn, baseProjection.Compose(x => x.PayloadValues.Length >= 1 ? (string) x.PayloadValues[0] : String.Empty));
+            tableGenerator.AddColumn(exceptionMessageColumn, baseProjection.Compose(x => x.PayloadValues.Length >= 2 ? (string) x.PayloadValues[1] : String.Empty));
+            tableGenerator.AddColumn(exceptionEIP, baseProjection.Compose(x => x.PayloadValues.Length >= 3 ? (ulong) x.PayloadValues[2] : 0));
+            tableGenerator.AddColumn(exceptionHRESULTColumn, baseProjection.Compose(x => x.PayloadValues.Length >= 4 ? (int) x.PayloadValues[3] : 0));
+            tableGenerator.AddColumn(exceptionFlagsColumn, baseProjection.Compose(x => x.PayloadValues.Length >= 5 ? (ExceptionThrownFlags) x.PayloadValues[4] : ExceptionThrownFlags.None));
+            tableGenerator.AddColumn(clrInstanceIDColumn, baseProjection.Compose(x => x.PayloadValues.Length >= 6 ? (int) x.PayloadValues[5] : 0));
+            tableGenerator.AddColumn(processIdColumn, baseProjection.Compose(x => x.ProcessID));
+            tableGenerator.AddColumn(processColumn, baseProjection.Compose(x => x.ProcessName));
+            tableGenerator.AddColumn(cpuColumn, baseProjection.Compose(x => x.ProcessorNumber));
+            tableGenerator.AddColumn(threadIdColumn, baseProjection.Compose(x => x.ThreadID));
+            tableGenerator.AddColumn(timestampColumn, baseProjection.Compose(x => x.Timestamp));
+            tableGenerator.AddColumn(hascallStackColumn, baseProjection.Compose(x => x.CallStack != null));
+            tableGenerator.AddHierarchicalColumn(callStackColumn, baseProjection.Compose(x => x.CallStack), new ArrayAccessProvider());
+
+            var exceptionsTableConfig = new TableConfiguration("Exceptions by Type, Callstack")
+            {
+                Columns = new ColumnConfiguration[]
+                {
+                    exceptionTypeColumn,
+                    callStackColumn,
+                    TableConfiguration.PivotColumn,
+                    exceptionMessageColumn,
+                    exceptionHRESULTColumn,
+                    processColumn,
+                    exceptionFlagsColumn,
+                    cpuColumn,
+                    threadIdColumn,
+                    countColumn,
+                    exceptionEIP,
+                    clrInstanceIDColumn,
+                    TableConfiguration.GraphColumn, // Columns after this get graphed
+                    timestampColumn,
+        }
+            };
+            exceptionsTableConfig.AddColumnRole(ColumnRole.StartTime, timestampColumn);
+            exceptionsTableConfig.AddColumnRole(ColumnRole.EndTime, timestampColumn);
+
+            var table = tableBuilder
+            .AddTableConfiguration(exceptionsTableConfig)
+            .SetDefaultTableConfiguration(exceptionsTableConfig);
+        }
+    }
+}
diff --git a/DotNetEventPipe/Tables/GCTable.cs b/DotNetEventPipe/Tables/GCTable.cs
new file mode 100644
index 0000000..0a2c9c7
--- /dev/null
+++ b/DotNetEventPipe/Tables/GCTable.cs
@@ -0,0 +1,172 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using DotNetEventPipe.DataOutputTypes;
+using Microsoft.Diagnostics.Tracing.Parsers.Clr;
+using Microsoft.Performance.SDK;
+using Microsoft.Performance.SDK.Processing;
+using Utilities;
+using Utilities.AccessProviders;
+using static Utilities.TimeHelper;
+
+namespace DotNetEventPipe.Tables
+{
+    //
+    // Have the MetadataTable inherit the TableBase class
+    //
+    [Table]              // A category is optional. It useful for grouping different types of tables
+    public sealed class GCTable
+        : TraceEventTableBase
+    {
+        public static readonly TableDescriptor TableDescriptor = new TableDescriptor(
+            Guid.Parse("{9D91E453-8ADD-4650-8C91-77E58B42BBBA}"),
+            "GC",
+            "Garbage Collector",
+            category: ".NET trace (dotnet-trace)");
+
+        public GCTable(IReadOnlyDictionary traceEventProcessor)
+            : base(traceEventProcessor)
+        {
+        }
+
+        private static readonly ColumnConfiguration countColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{847D85A8-77CB-4E4F-B664-4FAEF6394707}"), "Count", "Count"),
+            new UIHints
+            {
+                Width = 130,
+                AggregationMode = AggregationMode.Sum,
+                SortOrder = SortOrder.Descending,
+                SortPriority = 0,
+            });
+
+        private static readonly ColumnConfiguration gcReasonColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{39BEA7E7-C543-46FD-9FCF-6A7C249D35EC}"), "Reason", "GC Reason"),
+            new UIHints { Width = 125 });
+
+        private static readonly ColumnConfiguration depthColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{CB84FD81-24EF-49AA-91B0-4BB2D00BBE0E}"), "Depth", "Depth"),
+            new UIHints { Width = 80 });
+
+        private static readonly ColumnConfiguration gcTypeColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{096124B1-F46D-4204-8C0A-D4DA65ACEBDA}"), "Type", "GC Type"),
+            new UIHints { Width = 160 });
+
+        private static readonly ColumnConfiguration clrInstanceIDColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{FFE7E4B2-0636-403B-A2F6-0B6D696C18FC}"), "ClrInstanceID", "Clr Instance ID"),
+            new UIHints { Width = 115 });
+
+        private static readonly ColumnConfiguration clientSequenceNumberColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{105C1056-EC3B-4980-A36E-D0CBE1E4A479}"), "ClientSequenceNumber", "Client Sequence Number"),
+            new UIHints { Width = 145 });
+
+        private static readonly ColumnConfiguration timestampColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{00746811-C2AE-48B8-AF00-1213AFFB4D6F}"), "Timestamp", "The timestamp of the event"),
+            new UIHints { Width = 80 });
+
+        private static readonly ColumnConfiguration durationColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{2EFD88CE-0559-46D7-8E1A-43F5B2EDC482}"), "Duration", "Duration of the GC"),
+            new UIHints { Width = 80 });
+
+        private static readonly ColumnConfiguration threadIdColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{0CDD7D87-FDC3-457D-87CD-692EA4280664}"), "ThreadId"),
+                new UIHints { Width = 80, });
+
+        private static readonly ColumnConfiguration processColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{B3BA7352-86CC-4271-A136-04CA071D19C5}"), "Process"),
+                new UIHints { Width = 100, });
+
+        private static readonly ColumnConfiguration processIdColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{7F13B9C1-7FD5-417C-B768-637D7BF0CA55}"), "Process Id"),
+                new UIHints { Width = 80, });
+
+        private static readonly ColumnConfiguration cpuColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{5C9EAC74-23E2-4450-9CC0-65C516AC1422}"), "CPU"),
+                new UIHints { Width = 80, });
+
+
+        public override void Build(ITableBuilder tableBuilder)
+        {
+            if (TraceEventProcessor == null || TraceEventProcessor.Count == 0)
+            {
+                return;
+            }
+
+            var firstTraceProcessorEventsParsed = TraceEventProcessor.First().Value;  // First Log
+            var gcStartEvents = firstTraceProcessorEventsParsed.GenericEvents.Where(f => f.ProviderName == "Microsoft-Windows-DotNETRuntime" && 
+                                                                                   f.EventName == "GC/Start").ToArray();
+            var gcStopEvents = firstTraceProcessorEventsParsed.GenericEvents.Where(f => f.ProviderName == "Microsoft-Windows-DotNETRuntime" &&
+                                                                                   f.EventName == "GC/Stop").OrderBy(f => f.Timestamp).ToArray();
+
+            var tableGenerator = tableBuilder.SetRowCount(gcStartEvents.Length);
+            var baseProjection = Projection.Index(gcStartEvents);
+
+            var maximumFieldCount = 0;
+            foreach (var genericEvent in gcStartEvents)
+            {
+                maximumFieldCount = Math.Max(maximumFieldCount, genericEvent.PayloadValues.Length);
+            }
+
+            tableGenerator.AddColumn(countColumn, baseProjection.Compose(x => x.PayloadValues.Length >= 1 ? (int)x.PayloadValues[0] : 0));
+            tableGenerator.AddColumn(gcReasonColumn, baseProjection.Compose(x => x.PayloadValues.Length >= 2 ? (GCReason)x.PayloadValues[1] : GCReason.AllocSmall));
+            tableGenerator.AddColumn(depthColumn, baseProjection.Compose(x => x.PayloadValues.Length >= 3 ? (int)x.PayloadValues[2] : 0));
+            tableGenerator.AddColumn(gcTypeColumn, baseProjection.Compose(x => x.PayloadValues.Length >= 4 ? (GCType)x.PayloadValues[3] : 0));
+            tableGenerator.AddColumn(clrInstanceIDColumn, baseProjection.Compose(x => x.PayloadValues.Length >= 5 ? (int)x.PayloadValues[4] : 0));
+            tableGenerator.AddColumn(clientSequenceNumberColumn, baseProjection.Compose(x => x.PayloadValues.Length >= 6 ? (long)x.PayloadValues[5] : 0));
+            
+            tableGenerator.AddColumn(durationColumn, baseProjection.Compose(x => FindGCDuration(x, gcStopEvents)));
+            tableGenerator.AddColumn(processIdColumn, baseProjection.Compose(x => x.ProcessID));
+            tableGenerator.AddColumn(processColumn, baseProjection.Compose(x => x.ProcessName));
+            tableGenerator.AddColumn(cpuColumn, baseProjection.Compose(x => x.ProcessorNumber));
+            tableGenerator.AddColumn(threadIdColumn, baseProjection.Compose(x => x.ThreadID));
+            tableGenerator.AddColumn(timestampColumn, baseProjection.Compose(x => x.Timestamp));
+
+
+            var gcConfig = new TableConfiguration("GC")
+            {
+                Columns = new ColumnConfiguration[]
+                {
+                    gcReasonColumn,
+                    gcTypeColumn,
+                    TableConfiguration.PivotColumn,
+                    processColumn,
+                    cpuColumn,
+                    threadIdColumn,
+                    countColumn,
+                    depthColumn,
+                    clientSequenceNumberColumn,
+                    clrInstanceIDColumn,
+                    TableConfiguration.GraphColumn, // Columns after this get graphed
+                    timestampColumn,
+                    durationColumn,
+        }
+            };
+            gcConfig.AddColumnRole(ColumnRole.StartTime, timestampColumn);
+            gcConfig.AddColumnRole(ColumnRole.EndTime, timestampColumn);
+            gcConfig.AddColumnRole(ColumnRole.Duration, durationColumn);
+
+            var table = tableBuilder
+            .AddTableConfiguration(gcConfig)
+            .SetDefaultTableConfiguration(gcConfig);
+        }
+
+        TimestampDelta FindGCDuration(GenericEvent gcStart, IEnumerable gcStops)
+        {
+            var stop = gcStops.FirstOrDefault(f => f.ThreadID == gcStart.ThreadID && f.Timestamp > gcStart.Timestamp);
+            if (stop == null)
+            {
+                return TimestampDelta.Zero;
+            }
+            else
+            {
+                return stop.Timestamp - gcStart.Timestamp;
+            }
+        }
+    }
+}
diff --git a/DotNetEventPipe/Tables/GenericEventTable.cs b/DotNetEventPipe/Tables/GenericEventTable.cs
new file mode 100644
index 0000000..ffa68f3
--- /dev/null
+++ b/DotNetEventPipe/Tables/GenericEventTable.cs
@@ -0,0 +1,198 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Performance.SDK.Processing;
+using Utilities;
+using Utilities.AccessProviders;
+using static Utilities.TimeHelper;
+
+namespace DotNetEventPipe.Tables
+{
+    //
+    // Have the MetadataTable inherit the TableBase class
+    //
+    [Table]              // A category is optional. It useful for grouping different types of tables
+    public sealed class GenericEventTable
+        : TraceEventTableBase
+    {
+        public static readonly TableDescriptor TableDescriptor = new TableDescriptor(
+            Guid.Parse("{50315806-167F-46FF-946A-EA8B21F33F36}"),
+            "Generic Events",
+            "Generic Events",
+            category: ".NET trace (dotnet-trace)");
+
+        public GenericEventTable(IReadOnlyDictionary traceEventProcessor)
+            : base(traceEventProcessor)
+        {
+        }
+
+        private static readonly ColumnConfiguration eventNameColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{7AE1590E-20D3-4F39-90D1-4AEE5CD41394}"), "EventName", "Event Name"),
+            new UIHints { Width = 305 });
+
+        private static readonly ColumnConfiguration idColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{75FE88C4-CB8E-46E5-B8F4-CE760B8A6F51}"), "ID", "Event ID"),
+            new UIHints { Width = 80 });
+
+        private static readonly ColumnConfiguration keywordsColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{54145FD1-2EF3-4A6D-9914-44ECCBBF8532}"), "Keywords", "Keywords"),
+            new UIHints { Width = 80 });
+
+        private static readonly ColumnConfiguration levelColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{A2DCBDE1-4899-49E2-8096-BE34BB10674F}"), "Level", "Level"),
+            new UIHints { Width = 80 });
+
+        private static readonly ColumnConfiguration opcodeColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{520E92D2-F4DE-432E-80EE-8A557D5E6BD1}"), "Opcode", "Opcode"),
+            new UIHints { Width = 80 });
+
+        private static readonly ColumnConfiguration opcodeNameColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{912561AE-BDE2-4745-8504-D4E696C6F800}"), "OpcodeName", "Opcode Name"),
+            new UIHints { Width = 170 });
+
+        private static readonly ColumnConfiguration providerGuidColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{5C13098C-579C-443E-8476-3187FF532966}"), "ProviderGuid", "Provider Guid"),
+            new UIHints { Width = 80 });
+
+        private static readonly ColumnConfiguration providerNameColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{93C151D8-53D0-4AAF-AFDE-BBB2D516F59A}"), "Provider Name", "Provider Name"),
+            new UIHints { Width = 270 });
+
+        private static readonly ColumnConfiguration timestampColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{F5AE0530-DC46-41B3-A0E9-416955A9C7CA}"), "Timestamp", "The timestamp of the event"),
+            new UIHints { Width = 80 });
+
+
+        private static readonly ColumnConfiguration countColumn = new ColumnConfiguration(
+            new ColumnMetadata(new Guid("{06B669F7-A719-4B4C-9F1F-0B50B6D20EF7}"), "Count", "The count of samples"),
+            new UIHints { 
+                Width = 130, 
+                AggregationMode = AggregationMode.Sum,
+                SortOrder = SortOrder.Descending,
+                SortPriority = 0,
+            });
+
+        private static readonly ColumnConfiguration hascallStackColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{2BCEC64D-A3A2-4EC5-B376-4040560066FC}"), "HasCallstack", "Has Callstack"),
+                new UIHints { Width = 40, });
+
+        private static readonly ColumnConfiguration callStackColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{37EF26BF-8FAF-4700-AD66-01D90DA743BF}"), "Callstack", "Call stack"),
+                new UIHints { Width = 800, });
+
+        private static readonly ColumnConfiguration threadIdColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{4EEBBEF3-C5E5-422C-AF6D-B94CE17AB3DF}"), "ThreadId"),
+                new UIHints { Width = 80, });
+
+        private static readonly ColumnConfiguration processColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{E3FEEE4F-CECC-4BEA-A926-253869ADD223}"), "Process"),
+                new UIHints { Width = 80, });
+
+        private static readonly ColumnConfiguration processIdColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{E40AED70-CAF0-4B24-AE44-3EE02FD2A5BD}"), "Process Id"),
+                new UIHints { Width = 80, });
+
+        private static readonly ColumnConfiguration cpuColumn =
+            new ColumnConfiguration(
+                new ColumnMetadata(new Guid("{5AAB0226-0599-427C-B84F-0E1A86CE6B73}"), "CPU"),
+                new UIHints { Width = 80, });
+
+
+        public override void Build(ITableBuilder tableBuilder)
+        {
+            if (TraceEventProcessor == null || TraceEventProcessor.Count == 0)
+            {
+                return;
+            }
+
+            var firstTraceProcessorEventsParsed = TraceEventProcessor.First().Value;  // First Log
+            var genericEvents = firstTraceProcessorEventsParsed.GenericEvents;
+
+            var tableGenerator = tableBuilder.SetRowCount(genericEvents.Count);
+            var baseProjection = Projection.Index(genericEvents);
+
+            var maximumFieldCount = 0;
+            foreach (var genericEvent in genericEvents)
+            {
+                maximumFieldCount = Math.Max(maximumFieldCount, genericEvent.PayloadValues.Length);
+            }
+
+            tableGenerator.AddColumn(countColumn, baseProjection.Compose(x => 1));                  // 1 sample
+            tableGenerator.AddColumn(eventNameColumn, baseProjection.Compose(x => x.EventName));
+            tableGenerator.AddColumn(idColumn, baseProjection.Compose(x => x.ID));
+            tableGenerator.AddColumn(keywordsColumn, baseProjection.Compose(x => x.Keywords));
+            tableGenerator.AddColumn(levelColumn, baseProjection.Compose(x => x.Level));
+            tableGenerator.AddColumn(opcodeColumn, baseProjection.Compose(x => x.Opcode));
+            tableGenerator.AddColumn(opcodeNameColumn, baseProjection.Compose(x => x.OpcodeName));
+            tableGenerator.AddColumn(providerGuidColumn, baseProjection.Compose(x => x.ProviderGuid));
+            tableGenerator.AddColumn(providerNameColumn, baseProjection.Compose(x => x.ProviderName));
+            tableGenerator.AddColumn(processIdColumn, baseProjection.Compose(x => x.ProcessID));
+            tableGenerator.AddColumn(processColumn, baseProjection.Compose(x => x.ProcessName));
+            tableGenerator.AddColumn(cpuColumn, baseProjection.Compose(x => x.ProcessorNumber));
+            tableGenerator.AddColumn(threadIdColumn, baseProjection.Compose(x => x.ThreadID));
+            tableGenerator.AddColumn(timestampColumn, baseProjection.Compose(x => x.Timestamp));
+            tableGenerator.AddColumn(hascallStackColumn, baseProjection.Compose(x => x.CallStack != null));
+            tableGenerator.AddHierarchicalColumn(callStackColumn, baseProjection.Compose(x => x.CallStack), new ArrayAccessProvider());
+
+            // Add the field columns, with column names depending on the given event
+            List fieldColumns = new List();
+            for (int index = 0; index < maximumFieldCount; index++)
+            {
+                var colIndex = index;  // This seems unncessary but causes odd runtime behavior if not done this way. Compiler is confused perhaps because w/o this func will index=genericEvent.FieldNames.Count every time. index is passed as ref but colIndex as value into func
+                string fieldName = "Field " + (colIndex + 1);
+
+                var genericEventFieldNameProjection = baseProjection.Compose((genericEvent) => colIndex < genericEvent.PayloadNames?.Length ? genericEvent.PayloadNames[colIndex] : string.Empty);
+
+                // generate a column configuration
+                var fieldColumnConfiguration = new ColumnConfiguration(
+                        new ColumnMetadata(Common.GenerateGuidFromName(fieldName), fieldName, genericEventFieldNameProjection, fieldName),
+                        new UIHints
+                        {
+                            IsVisible = true,
+                            Width = 80,
+                            TextAlignment = TextAlignment.Left,
+                        });
+                fieldColumns.Add(fieldColumnConfiguration);
+
+                var genericEventFieldAsStringProjection = baseProjection.Compose((genericEvent) => colIndex < genericEvent.PayloadValues?.Length ? genericEvent.PayloadValues[colIndex] : string.Empty);
+
+                tableGenerator.AddColumn(fieldColumnConfiguration, genericEventFieldAsStringProjection);
+            }
+
+
+            List defaultColumns = new List()
+            {
+                providerNameColumn,
+                eventNameColumn,
+                opcodeNameColumn,
+                TableConfiguration.PivotColumn,
+                cpuColumn,
+                processColumn,
+                threadIdColumn,
+            };
+            defaultColumns.AddRange(fieldColumns);
+            defaultColumns.Add(countColumn);
+            defaultColumns.Add(TableConfiguration.GraphColumn); // Columns after this get graphed
+            defaultColumns.Add(timestampColumn);
+
+            var actProviderNameOpcode = new TableConfiguration("Activity by Provider, EventName, Opcode")
+            {
+                Columns = defaultColumns
+            };
+            actProviderNameOpcode.AddColumnRole(ColumnRole.StartTime, timestampColumn);
+            actProviderNameOpcode.AddColumnRole(ColumnRole.EndTime, timestampColumn);
+
+            var table = tableBuilder
+            .AddTableConfiguration(actProviderNameOpcode)
+            .SetDefaultTableConfiguration(actProviderNameOpcode);
+        }
+    }
+}
diff --git a/DotNetEventPipe/Tables/TraceEventTableBase.cs b/DotNetEventPipe/Tables/TraceEventTableBase.cs
new file mode 100644
index 0000000..2f7523d
--- /dev/null
+++ b/DotNetEventPipe/Tables/TraceEventTableBase.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Generic;
+using Microsoft.Diagnostics.Tracing.StackSources;
+using Microsoft.Performance.SDK.Processing;
+
+namespace DotNetEventPipe.Tables
+{
+    public abstract class TraceEventTableBase
+    {
+        protected TraceEventTableBase(IReadOnlyDictionary traceEventProcessor)
+        {
+            this.TraceEventProcessor = traceEventProcessor;
+        }
+
+        //
+        // In this sample we are going to assume the files will fit in memory,
+        // and so we will make sure all tables have access to the collection of lines in the file.
+        //
+
+        public IReadOnlyDictionary TraceEventProcessor { get; }
+
+        //
+        // All tables will need some way to build themselves via the ITableBuilder interface.
+        //
+
+        public abstract void Build(ITableBuilder tableBuilder);
+    }
+}
diff --git a/DotnetEventpipeTest/DotnetEventpipeTest.csproj b/DotnetEventpipeTest/DotnetEventpipeTest.csproj
new file mode 100644
index 0000000..99d6059
--- /dev/null
+++ b/DotnetEventpipeTest/DotnetEventpipeTest.csproj
@@ -0,0 +1,22 @@
+
+
+  
+    netcoreapp3.1
+
+    false
+  
+
+  
+    
+    
+    
+    
+    
+  
+
+  
+    
+    
+  
+
+
diff --git a/DotnetEventpipeTest/DotnetTraceTests.cs b/DotnetEventpipeTest/DotnetTraceTests.cs
new file mode 100644
index 0000000..82e78df
--- /dev/null
+++ b/DotnetEventpipeTest/DotnetTraceTests.cs
@@ -0,0 +1,64 @@
+using DotNetEventPipe.Tables;
+using Microsoft.Performance.SDK.Processing;
+using Microsoft.Performance.Toolkit.Engine;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.IO;
+using UnitTestCommon;
+
+namespace DotnetEventpipeTest
+{
+    [TestClass]
+    public class DotnetTraceTests
+    {
+        public static bool IsTraceProcessed = false;
+        public static object IsTraceProcessedLock = new object();
+
+        private static RuntimeExecutionResults RuntimeExecutionResults;
+
+
+        public static void ProcessTrace()
+        {
+            lock (IsTraceProcessedLock)
+            {
+                if (!IsTraceProcessed)
+                {
+                    // Input data
+                    string[] dotnetTraceData = { @"..\..\..\..\TestData\Dotnet-Trace\HelloWorld_GC_Threads_Exception.nettrace" };
+                    var dotnetTraceDataPath = new FileInfo(dotnetTraceData[0]);
+                    Assert.IsTrue(dotnetTraceDataPath.Exists);
+
+                    // Approach #1 - Engine - Doesn't test tables UI but tests processing
+                    var runtime = Engine.Create(new FileDataSource(dotnetTraceDataPath.FullName));
+
+                    // Enable our various types of data
+                    // Need to use cookers for this to work
+
+                    // Enable tables used by UI
+                    runtime.EnableTable(CpuSamplingTable.TableDescriptor);
+                    runtime.EnableTable(GenericEventTable.TableDescriptor);
+                    runtime.EnableTable(ExceptionTable.TableDescriptor);
+                    runtime.EnableTable(GCTable.TableDescriptor);
+
+                    //
+                    // Process our data.
+                    //
+
+                    RuntimeExecutionResults = runtime.Process();
+                    UnitTest.TestTableBuild(RuntimeExecutionResults, CpuSamplingTable.TableDescriptor, 39);
+                    UnitTest.TestTableBuild(RuntimeExecutionResults, GenericEventTable.TableDescriptor, 421);
+                    UnitTest.TestTableBuild(RuntimeExecutionResults, ExceptionTable.TableDescriptor, 1);
+                    UnitTest.TestTableBuild(RuntimeExecutionResults, GCTable.TableDescriptor, 1);
+
+                    IsTraceProcessed = true;
+                }
+            }
+        }
+
+        [TestMethod]
+        public void DotnetTraceTest()
+        {
+            ProcessTrace();
+        }
+    }
+}
diff --git a/LTTngDataExtUnitTest/LTTngDataExtUnitTest.csproj b/LTTngDataExtUnitTest/LTTngDataExtUnitTest.csproj
index af3ce5c..db0d96c 100644
--- a/LTTngDataExtUnitTest/LTTngDataExtUnitTest.csproj
+++ b/LTTngDataExtUnitTest/LTTngDataExtUnitTest.csproj
@@ -7,13 +7,13 @@
   
 
   
-    
+    
     
     
     
-    
-    
-    
+    
+    
+    
     
       all
       runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/LinuxLogParsers/LinuxLogParsersUnitTest/LinuxLogParsersUnitTest.csproj b/LinuxLogParsers/LinuxLogParsersUnitTest/LinuxLogParsersUnitTest.csproj
index f254dd6..f4329f8 100644
--- a/LinuxLogParsers/LinuxLogParsersUnitTest/LinuxLogParsersUnitTest.csproj
+++ b/LinuxLogParsers/LinuxLogParsersUnitTest/LinuxLogParsersUnitTest.csproj
@@ -7,12 +7,12 @@
   
 
   
-    
+    
     
     
-    
-    
-    
+    
+    
+    
     
       all
       runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/LinuxTraceLogCapture.md b/LinuxTraceLogCapture.md
index a7533d2..02e9564 100644
--- a/LinuxTraceLogCapture.md
+++ b/LinuxTraceLogCapture.md
@@ -16,6 +16,7 @@ Logs:
   - LogLevel can be turned more verbose in custom image
   - /etc/waagent.conf
   - Logs.Verbose=y
+- [dotnet-trace (.nettrace)](https://docs.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-trace)
 - [AndroidLogcat](https://developer.android.com/studio/command-line/logcat)
   - Default log format should be supported
   - Basic durations are supported/parsed on production builds / logs.
diff --git a/Microsoft-Perf-Tools-Linux-Android.sln b/Microsoft-Perf-Tools-Linux-Android.sln
index 27815d4..4ac94f8 100644
--- a/Microsoft-Perf-Tools-Linux-Android.sln
+++ b/Microsoft-Perf-Tools-Linux-Android.sln
@@ -1,180 +1,194 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.0.31903.59
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CtfPlayback", "CtfPlayback\CtfPlayback.csproj", "{85A74448-3940-481D-B449-EF7FCAC02290}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LTTngCds", "LTTngCds\LTTngCds.csproj", "{E4EB869C-DBF9-4C7F-95BA-6D28DBC69FD0}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LTTngDataExtensions", "LTTngDataExtensions\LTTngDataExtensions.csproj", "{8EE62907-1246-48FA-9AD5-D1DFEC7F26F8}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CtfUnitTest", "CtfUnitTest\CtfUnitTest.csproj", "{FC2236A8-FBE7-43BC-86A1-CBC364DFDF91}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfCds", "PerfCds\PerfCds.csproj", "{DF90DB4D-93B8-498F-8DA9-557495BBFCB6}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfDataExtensions", "PerfDataExtensions\PerfDataExtensions.csproj", "{B381F72E-335C-4842-9DB2-615D5C18A943}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LinuxLogParsers", "LinuxLogParsers", "{9B2D4167-1CC7-4D8B-A01C-E9919E809A0A}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LinuxLogParserCore", "LinuxLogParsers\LinuxLogParserCore\LinuxLogParserCore.csproj", "{5B8667E9-0CB6-4562-B2BD-B5E7768BC1A4}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LinuxLogParser", "LinuxLogParsers\LinuxLogParser\LinuxLogParser.csproj", "{03DBB240-C4DD-4FF4-9405-F50CBF0A960D}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cloud-Init", "LinuxLogParsers\LinuxPlugins-MicrosoftPerformanceToolkSDK\Cloud-init\Cloud-Init.csproj", "{7CBFAE62-0D0D-4075-A426-99945DD4E9A8}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dmesg", "LinuxLogParsers\LinuxPlugins-MicrosoftPerformanceToolkSDK\DmesgIsoLog\Dmesg.csproj", "{04455C36-F127-4D40-BCB3-78E7DE21E70E}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WaLinuxAgent", "LinuxLogParsers\LinuxPlugins-MicrosoftPerformanceToolkSDK\WaLinuxAgent\WaLinuxAgent.csproj", "{75AF82A4-AF0E-425F-8CF5-62F4F54380B9}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{E48222FC-D167-4281-BC94-961C22908B25}"
-	ProjectSection(SolutionItems) = preProject
-		CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md
-		LinuxTraceLogCapture.md = LinuxTraceLogCapture.md
-		NOTICE.md = NOTICE.md
-		README.md = README.md
-		SECURITY.md = SECURITY.md
-		SUPPORT.md = SUPPORT.md
-	EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LinuxLogParsersUnitTest", "LinuxLogParsers\LinuxLogParsersUnitTest\LinuxLogParsersUnitTest.csproj", "{F910A8EA-9B0F-4BDA-87D4-0765EE973421}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LTTngDriver", "LTTngDriver\LTTngDriver.csproj", "{355F2F1D-8500-4DEA-994E-D456771247CB}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTestCommon", "UnitTestCommon\UnitTestCommon.csproj", "{20968FBC-74B7-4A65-9936-9F9C2EF8771B}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LTTngDataExtUnitTest", "LTTngDataExtUnitTest\LTTngDataExtUnitTest.csproj", "{5750B61D-C1FD-46E7-A175-DB515C6185B0}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfUnitTest", "PerfUnitTest\PerfUnitTest.csproj", "{159F637D-6AB1-4E7F-878E-E2C46CF2D920}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfettoCds", "PerfettoCds\PerfettoCds.csproj", "{83418D84-CACE-40E8-A0C3-8EEAF7E71969}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfettoProcessor", "PerfettoProcessor\PerfettoProcessor.csproj", "{90FE5422-631F-4B5A-8EF7-88FE542739DF}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfettoUnitTest", "PerfettoUnitTest\PerfettoUnitTest.csproj", "{CF89184C-8CE8-4656-9C5C-3F6C547EF2A3}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utilities", "Utilities\Utilities.csproj", "{0BA62F1B-1456-48FD-B2DE-C9FE1CB94B8B}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Launcher", "Launcher", "{6A9515FC-D4B2-4221-8E74-78E7AFF0E421}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Windows", "Windows", "{DB7FCF02-C949-4545-B58C-F514E85AB1FA}"
-	ProjectSection(SolutionItems) = preProject
-		Launcher\Windows\LaunchWpaPerfToolsLinuxAndroid.bat = Launcher\Windows\LaunchWpaPerfToolsLinuxAndroid.bat
-		Launcher\Windows\LaunchWpaPerfToolsLinuxAndroid.ps1 = Launcher\Windows\LaunchWpaPerfToolsLinuxAndroid.ps1
-		Launcher\Windows\MicrosoftPerfToolsLinuxAndroid.url = Launcher\Windows\MicrosoftPerfToolsLinuxAndroid.url
-	EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{38D9C39A-AB30-4DED-BA93-2638388FC57C}"
-	ProjectSection(SolutionItems) = preProject
-		azure-pipelines.yml = azure-pipelines.yml
-	EndProjectSection
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AndroidLogcat", "LinuxLogParsers\LinuxPlugins-MicrosoftPerformanceToolkSDK\AndroidLogCat\AndroidLogcat.csproj", "{9FD0BECC-AC5E-46A8-9749-46CD82BA89DE}"
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		Debug|Any CPU = Debug|Any CPU
-		Release|Any CPU = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{85A74448-3940-481D-B449-EF7FCAC02290}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{85A74448-3940-481D-B449-EF7FCAC02290}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{85A74448-3940-481D-B449-EF7FCAC02290}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{85A74448-3940-481D-B449-EF7FCAC02290}.Release|Any CPU.Build.0 = Release|Any CPU
-		{E4EB869C-DBF9-4C7F-95BA-6D28DBC69FD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{E4EB869C-DBF9-4C7F-95BA-6D28DBC69FD0}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{E4EB869C-DBF9-4C7F-95BA-6D28DBC69FD0}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{E4EB869C-DBF9-4C7F-95BA-6D28DBC69FD0}.Release|Any CPU.Build.0 = Release|Any CPU
-		{8EE62907-1246-48FA-9AD5-D1DFEC7F26F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{8EE62907-1246-48FA-9AD5-D1DFEC7F26F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{8EE62907-1246-48FA-9AD5-D1DFEC7F26F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{8EE62907-1246-48FA-9AD5-D1DFEC7F26F8}.Release|Any CPU.Build.0 = Release|Any CPU
-		{FC2236A8-FBE7-43BC-86A1-CBC364DFDF91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{FC2236A8-FBE7-43BC-86A1-CBC364DFDF91}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{FC2236A8-FBE7-43BC-86A1-CBC364DFDF91}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{FC2236A8-FBE7-43BC-86A1-CBC364DFDF91}.Release|Any CPU.Build.0 = Release|Any CPU
-		{DF90DB4D-93B8-498F-8DA9-557495BBFCB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{DF90DB4D-93B8-498F-8DA9-557495BBFCB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{DF90DB4D-93B8-498F-8DA9-557495BBFCB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{DF90DB4D-93B8-498F-8DA9-557495BBFCB6}.Release|Any CPU.Build.0 = Release|Any CPU
-		{B381F72E-335C-4842-9DB2-615D5C18A943}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{B381F72E-335C-4842-9DB2-615D5C18A943}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{B381F72E-335C-4842-9DB2-615D5C18A943}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{B381F72E-335C-4842-9DB2-615D5C18A943}.Release|Any CPU.Build.0 = Release|Any CPU
-		{5B8667E9-0CB6-4562-B2BD-B5E7768BC1A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{5B8667E9-0CB6-4562-B2BD-B5E7768BC1A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{5B8667E9-0CB6-4562-B2BD-B5E7768BC1A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{5B8667E9-0CB6-4562-B2BD-B5E7768BC1A4}.Release|Any CPU.Build.0 = Release|Any CPU
-		{03DBB240-C4DD-4FF4-9405-F50CBF0A960D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{03DBB240-C4DD-4FF4-9405-F50CBF0A960D}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{03DBB240-C4DD-4FF4-9405-F50CBF0A960D}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{03DBB240-C4DD-4FF4-9405-F50CBF0A960D}.Release|Any CPU.Build.0 = Release|Any CPU
-		{7CBFAE62-0D0D-4075-A426-99945DD4E9A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{7CBFAE62-0D0D-4075-A426-99945DD4E9A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{7CBFAE62-0D0D-4075-A426-99945DD4E9A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{7CBFAE62-0D0D-4075-A426-99945DD4E9A8}.Release|Any CPU.Build.0 = Release|Any CPU
-		{04455C36-F127-4D40-BCB3-78E7DE21E70E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{04455C36-F127-4D40-BCB3-78E7DE21E70E}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{04455C36-F127-4D40-BCB3-78E7DE21E70E}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{04455C36-F127-4D40-BCB3-78E7DE21E70E}.Release|Any CPU.Build.0 = Release|Any CPU
-		{75AF82A4-AF0E-425F-8CF5-62F4F54380B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{75AF82A4-AF0E-425F-8CF5-62F4F54380B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{75AF82A4-AF0E-425F-8CF5-62F4F54380B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{75AF82A4-AF0E-425F-8CF5-62F4F54380B9}.Release|Any CPU.Build.0 = Release|Any CPU
-		{F910A8EA-9B0F-4BDA-87D4-0765EE973421}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{F910A8EA-9B0F-4BDA-87D4-0765EE973421}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{F910A8EA-9B0F-4BDA-87D4-0765EE973421}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{F910A8EA-9B0F-4BDA-87D4-0765EE973421}.Release|Any CPU.Build.0 = Release|Any CPU
-		{355F2F1D-8500-4DEA-994E-D456771247CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{355F2F1D-8500-4DEA-994E-D456771247CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{355F2F1D-8500-4DEA-994E-D456771247CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{355F2F1D-8500-4DEA-994E-D456771247CB}.Release|Any CPU.Build.0 = Release|Any CPU
-		{20968FBC-74B7-4A65-9936-9F9C2EF8771B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{20968FBC-74B7-4A65-9936-9F9C2EF8771B}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{20968FBC-74B7-4A65-9936-9F9C2EF8771B}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{20968FBC-74B7-4A65-9936-9F9C2EF8771B}.Release|Any CPU.Build.0 = Release|Any CPU
-		{5750B61D-C1FD-46E7-A175-DB515C6185B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{5750B61D-C1FD-46E7-A175-DB515C6185B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{5750B61D-C1FD-46E7-A175-DB515C6185B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{5750B61D-C1FD-46E7-A175-DB515C6185B0}.Release|Any CPU.Build.0 = Release|Any CPU
-		{159F637D-6AB1-4E7F-878E-E2C46CF2D920}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{159F637D-6AB1-4E7F-878E-E2C46CF2D920}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{159F637D-6AB1-4E7F-878E-E2C46CF2D920}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{159F637D-6AB1-4E7F-878E-E2C46CF2D920}.Release|Any CPU.Build.0 = Release|Any CPU
-		{83418D84-CACE-40E8-A0C3-8EEAF7E71969}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{83418D84-CACE-40E8-A0C3-8EEAF7E71969}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{83418D84-CACE-40E8-A0C3-8EEAF7E71969}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{83418D84-CACE-40E8-A0C3-8EEAF7E71969}.Release|Any CPU.Build.0 = Release|Any CPU
-		{90FE5422-631F-4B5A-8EF7-88FE542739DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{90FE5422-631F-4B5A-8EF7-88FE542739DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{90FE5422-631F-4B5A-8EF7-88FE542739DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{90FE5422-631F-4B5A-8EF7-88FE542739DF}.Release|Any CPU.Build.0 = Release|Any CPU
-		{CF89184C-8CE8-4656-9C5C-3F6C547EF2A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{CF89184C-8CE8-4656-9C5C-3F6C547EF2A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{CF89184C-8CE8-4656-9C5C-3F6C547EF2A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{CF89184C-8CE8-4656-9C5C-3F6C547EF2A3}.Release|Any CPU.Build.0 = Release|Any CPU
-		{0BA62F1B-1456-48FD-B2DE-C9FE1CB94B8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{0BA62F1B-1456-48FD-B2DE-C9FE1CB94B8B}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{0BA62F1B-1456-48FD-B2DE-C9FE1CB94B8B}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{0BA62F1B-1456-48FD-B2DE-C9FE1CB94B8B}.Release|Any CPU.Build.0 = Release|Any CPU
-		{9FD0BECC-AC5E-46A8-9749-46CD82BA89DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{9FD0BECC-AC5E-46A8-9749-46CD82BA89DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{9FD0BECC-AC5E-46A8-9749-46CD82BA89DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{9FD0BECC-AC5E-46A8-9749-46CD82BA89DE}.Release|Any CPU.Build.0 = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-	GlobalSection(NestedProjects) = preSolution
-		{5B8667E9-0CB6-4562-B2BD-B5E7768BC1A4} = {9B2D4167-1CC7-4D8B-A01C-E9919E809A0A}
-		{03DBB240-C4DD-4FF4-9405-F50CBF0A960D} = {9B2D4167-1CC7-4D8B-A01C-E9919E809A0A}
-		{7CBFAE62-0D0D-4075-A426-99945DD4E9A8} = {9B2D4167-1CC7-4D8B-A01C-E9919E809A0A}
-		{04455C36-F127-4D40-BCB3-78E7DE21E70E} = {9B2D4167-1CC7-4D8B-A01C-E9919E809A0A}
-		{75AF82A4-AF0E-425F-8CF5-62F4F54380B9} = {9B2D4167-1CC7-4D8B-A01C-E9919E809A0A}
-		{F910A8EA-9B0F-4BDA-87D4-0765EE973421} = {9B2D4167-1CC7-4D8B-A01C-E9919E809A0A}
-		{DB7FCF02-C949-4545-B58C-F514E85AB1FA} = {6A9515FC-D4B2-4221-8E74-78E7AFF0E421}
-		{9FD0BECC-AC5E-46A8-9749-46CD82BA89DE} = {9B2D4167-1CC7-4D8B-A01C-E9919E809A0A}
-	EndGlobalSection
-	GlobalSection(ExtensibilityGlobals) = postSolution
-		SolutionGuid = {E96603EA-8E1D-4AA9-A474-D267A116C316}
-	EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CtfPlayback", "CtfPlayback\CtfPlayback.csproj", "{85A74448-3940-481D-B449-EF7FCAC02290}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LTTngCds", "LTTngCds\LTTngCds.csproj", "{E4EB869C-DBF9-4C7F-95BA-6D28DBC69FD0}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LTTngDataExtensions", "LTTngDataExtensions\LTTngDataExtensions.csproj", "{8EE62907-1246-48FA-9AD5-D1DFEC7F26F8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CtfUnitTest", "CtfUnitTest\CtfUnitTest.csproj", "{FC2236A8-FBE7-43BC-86A1-CBC364DFDF91}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfCds", "PerfCds\PerfCds.csproj", "{DF90DB4D-93B8-498F-8DA9-557495BBFCB6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfDataExtensions", "PerfDataExtensions\PerfDataExtensions.csproj", "{B381F72E-335C-4842-9DB2-615D5C18A943}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LinuxLogParsers", "LinuxLogParsers", "{9B2D4167-1CC7-4D8B-A01C-E9919E809A0A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LinuxLogParserCore", "LinuxLogParsers\LinuxLogParserCore\LinuxLogParserCore.csproj", "{5B8667E9-0CB6-4562-B2BD-B5E7768BC1A4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LinuxLogParser", "LinuxLogParsers\LinuxLogParser\LinuxLogParser.csproj", "{03DBB240-C4DD-4FF4-9405-F50CBF0A960D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cloud-Init", "LinuxLogParsers\LinuxPlugins-MicrosoftPerformanceToolkSDK\Cloud-init\Cloud-Init.csproj", "{7CBFAE62-0D0D-4075-A426-99945DD4E9A8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dmesg", "LinuxLogParsers\LinuxPlugins-MicrosoftPerformanceToolkSDK\DmesgIsoLog\Dmesg.csproj", "{04455C36-F127-4D40-BCB3-78E7DE21E70E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WaLinuxAgent", "LinuxLogParsers\LinuxPlugins-MicrosoftPerformanceToolkSDK\WaLinuxAgent\WaLinuxAgent.csproj", "{75AF82A4-AF0E-425F-8CF5-62F4F54380B9}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{E48222FC-D167-4281-BC94-961C22908B25}"
+	ProjectSection(SolutionItems) = preProject
+		CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md
+		DeveloperInfo.md = DeveloperInfo.md
+		LinuxTraceLogCapture.md = LinuxTraceLogCapture.md
+		NOTICE.md = NOTICE.md
+		README.md = README.md
+		SECURITY.md = SECURITY.md
+		SUPPORT.md = SUPPORT.md
+	EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LinuxLogParsersUnitTest", "LinuxLogParsers\LinuxLogParsersUnitTest\LinuxLogParsersUnitTest.csproj", "{F910A8EA-9B0F-4BDA-87D4-0765EE973421}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LTTngDriver", "LTTngDriver\LTTngDriver.csproj", "{355F2F1D-8500-4DEA-994E-D456771247CB}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTestCommon", "UnitTestCommon\UnitTestCommon.csproj", "{20968FBC-74B7-4A65-9936-9F9C2EF8771B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LTTngDataExtUnitTest", "LTTngDataExtUnitTest\LTTngDataExtUnitTest.csproj", "{5750B61D-C1FD-46E7-A175-DB515C6185B0}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfUnitTest", "PerfUnitTest\PerfUnitTest.csproj", "{159F637D-6AB1-4E7F-878E-E2C46CF2D920}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfettoCds", "PerfettoCds\PerfettoCds.csproj", "{83418D84-CACE-40E8-A0C3-8EEAF7E71969}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfettoProcessor", "PerfettoProcessor\PerfettoProcessor.csproj", "{90FE5422-631F-4B5A-8EF7-88FE542739DF}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfettoUnitTest", "PerfettoUnitTest\PerfettoUnitTest.csproj", "{CF89184C-8CE8-4656-9C5C-3F6C547EF2A3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utilities", "Utilities\Utilities.csproj", "{0BA62F1B-1456-48FD-B2DE-C9FE1CB94B8B}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Launcher", "Launcher", "{6A9515FC-D4B2-4221-8E74-78E7AFF0E421}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Windows", "Windows", "{DB7FCF02-C949-4545-B58C-F514E85AB1FA}"
+	ProjectSection(SolutionItems) = preProject
+		Launcher\Windows\LaunchWpaPerfToolsLinuxAndroid.bat = Launcher\Windows\LaunchWpaPerfToolsLinuxAndroid.bat
+		Launcher\Windows\LaunchWpaPerfToolsLinuxAndroid.ps1 = Launcher\Windows\LaunchWpaPerfToolsLinuxAndroid.ps1
+		Launcher\Windows\MicrosoftPerfToolsLinuxAndroid.url = Launcher\Windows\MicrosoftPerfToolsLinuxAndroid.url
+	EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{38D9C39A-AB30-4DED-BA93-2638388FC57C}"
+	ProjectSection(SolutionItems) = preProject
+		azure-pipelines.yml = azure-pipelines.yml
+	EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AndroidLogcat", "LinuxLogParsers\LinuxPlugins-MicrosoftPerformanceToolkSDK\AndroidLogCat\AndroidLogcat.csproj", "{9FD0BECC-AC5E-46A8-9749-46CD82BA89DE}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetEventpipe", "DotNetEventPipe\DotnetEventpipe.csproj", "{863E041F-0715-4B08-A1B8-B5CFC027ED6B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetEventpipeTest", "DotnetEventpipeTest\DotnetEventpipeTest.csproj", "{46861BDC-350C-45ED-A72E-9661C3BBE2D8}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{85A74448-3940-481D-B449-EF7FCAC02290}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{85A74448-3940-481D-B449-EF7FCAC02290}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{85A74448-3940-481D-B449-EF7FCAC02290}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{85A74448-3940-481D-B449-EF7FCAC02290}.Release|Any CPU.Build.0 = Release|Any CPU
+		{E4EB869C-DBF9-4C7F-95BA-6D28DBC69FD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E4EB869C-DBF9-4C7F-95BA-6D28DBC69FD0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E4EB869C-DBF9-4C7F-95BA-6D28DBC69FD0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E4EB869C-DBF9-4C7F-95BA-6D28DBC69FD0}.Release|Any CPU.Build.0 = Release|Any CPU
+		{8EE62907-1246-48FA-9AD5-D1DFEC7F26F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{8EE62907-1246-48FA-9AD5-D1DFEC7F26F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8EE62907-1246-48FA-9AD5-D1DFEC7F26F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{8EE62907-1246-48FA-9AD5-D1DFEC7F26F8}.Release|Any CPU.Build.0 = Release|Any CPU
+		{FC2236A8-FBE7-43BC-86A1-CBC364DFDF91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{FC2236A8-FBE7-43BC-86A1-CBC364DFDF91}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{FC2236A8-FBE7-43BC-86A1-CBC364DFDF91}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{FC2236A8-FBE7-43BC-86A1-CBC364DFDF91}.Release|Any CPU.Build.0 = Release|Any CPU
+		{DF90DB4D-93B8-498F-8DA9-557495BBFCB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{DF90DB4D-93B8-498F-8DA9-557495BBFCB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{DF90DB4D-93B8-498F-8DA9-557495BBFCB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{DF90DB4D-93B8-498F-8DA9-557495BBFCB6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{B381F72E-335C-4842-9DB2-615D5C18A943}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B381F72E-335C-4842-9DB2-615D5C18A943}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B381F72E-335C-4842-9DB2-615D5C18A943}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B381F72E-335C-4842-9DB2-615D5C18A943}.Release|Any CPU.Build.0 = Release|Any CPU
+		{5B8667E9-0CB6-4562-B2BD-B5E7768BC1A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5B8667E9-0CB6-4562-B2BD-B5E7768BC1A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5B8667E9-0CB6-4562-B2BD-B5E7768BC1A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{5B8667E9-0CB6-4562-B2BD-B5E7768BC1A4}.Release|Any CPU.Build.0 = Release|Any CPU
+		{03DBB240-C4DD-4FF4-9405-F50CBF0A960D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{03DBB240-C4DD-4FF4-9405-F50CBF0A960D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{03DBB240-C4DD-4FF4-9405-F50CBF0A960D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{03DBB240-C4DD-4FF4-9405-F50CBF0A960D}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7CBFAE62-0D0D-4075-A426-99945DD4E9A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7CBFAE62-0D0D-4075-A426-99945DD4E9A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7CBFAE62-0D0D-4075-A426-99945DD4E9A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7CBFAE62-0D0D-4075-A426-99945DD4E9A8}.Release|Any CPU.Build.0 = Release|Any CPU
+		{04455C36-F127-4D40-BCB3-78E7DE21E70E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{04455C36-F127-4D40-BCB3-78E7DE21E70E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{04455C36-F127-4D40-BCB3-78E7DE21E70E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{04455C36-F127-4D40-BCB3-78E7DE21E70E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{75AF82A4-AF0E-425F-8CF5-62F4F54380B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{75AF82A4-AF0E-425F-8CF5-62F4F54380B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{75AF82A4-AF0E-425F-8CF5-62F4F54380B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{75AF82A4-AF0E-425F-8CF5-62F4F54380B9}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F910A8EA-9B0F-4BDA-87D4-0765EE973421}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F910A8EA-9B0F-4BDA-87D4-0765EE973421}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F910A8EA-9B0F-4BDA-87D4-0765EE973421}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F910A8EA-9B0F-4BDA-87D4-0765EE973421}.Release|Any CPU.Build.0 = Release|Any CPU
+		{355F2F1D-8500-4DEA-994E-D456771247CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{355F2F1D-8500-4DEA-994E-D456771247CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{355F2F1D-8500-4DEA-994E-D456771247CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{355F2F1D-8500-4DEA-994E-D456771247CB}.Release|Any CPU.Build.0 = Release|Any CPU
+		{20968FBC-74B7-4A65-9936-9F9C2EF8771B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{20968FBC-74B7-4A65-9936-9F9C2EF8771B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{20968FBC-74B7-4A65-9936-9F9C2EF8771B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{20968FBC-74B7-4A65-9936-9F9C2EF8771B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{5750B61D-C1FD-46E7-A175-DB515C6185B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5750B61D-C1FD-46E7-A175-DB515C6185B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5750B61D-C1FD-46E7-A175-DB515C6185B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{5750B61D-C1FD-46E7-A175-DB515C6185B0}.Release|Any CPU.Build.0 = Release|Any CPU
+		{159F637D-6AB1-4E7F-878E-E2C46CF2D920}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{159F637D-6AB1-4E7F-878E-E2C46CF2D920}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{159F637D-6AB1-4E7F-878E-E2C46CF2D920}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{159F637D-6AB1-4E7F-878E-E2C46CF2D920}.Release|Any CPU.Build.0 = Release|Any CPU
+		{83418D84-CACE-40E8-A0C3-8EEAF7E71969}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{83418D84-CACE-40E8-A0C3-8EEAF7E71969}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{83418D84-CACE-40E8-A0C3-8EEAF7E71969}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{83418D84-CACE-40E8-A0C3-8EEAF7E71969}.Release|Any CPU.Build.0 = Release|Any CPU
+		{90FE5422-631F-4B5A-8EF7-88FE542739DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{90FE5422-631F-4B5A-8EF7-88FE542739DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{90FE5422-631F-4B5A-8EF7-88FE542739DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{90FE5422-631F-4B5A-8EF7-88FE542739DF}.Release|Any CPU.Build.0 = Release|Any CPU
+		{CF89184C-8CE8-4656-9C5C-3F6C547EF2A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{CF89184C-8CE8-4656-9C5C-3F6C547EF2A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{CF89184C-8CE8-4656-9C5C-3F6C547EF2A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{CF89184C-8CE8-4656-9C5C-3F6C547EF2A3}.Release|Any CPU.Build.0 = Release|Any CPU
+		{0BA62F1B-1456-48FD-B2DE-C9FE1CB94B8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{0BA62F1B-1456-48FD-B2DE-C9FE1CB94B8B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{0BA62F1B-1456-48FD-B2DE-C9FE1CB94B8B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{0BA62F1B-1456-48FD-B2DE-C9FE1CB94B8B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{9FD0BECC-AC5E-46A8-9749-46CD82BA89DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{9FD0BECC-AC5E-46A8-9749-46CD82BA89DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{9FD0BECC-AC5E-46A8-9749-46CD82BA89DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{9FD0BECC-AC5E-46A8-9749-46CD82BA89DE}.Release|Any CPU.Build.0 = Release|Any CPU
+		{863E041F-0715-4B08-A1B8-B5CFC027ED6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{863E041F-0715-4B08-A1B8-B5CFC027ED6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{863E041F-0715-4B08-A1B8-B5CFC027ED6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{863E041F-0715-4B08-A1B8-B5CFC027ED6B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{46861BDC-350C-45ED-A72E-9661C3BBE2D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{46861BDC-350C-45ED-A72E-9661C3BBE2D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{46861BDC-350C-45ED-A72E-9661C3BBE2D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{46861BDC-350C-45ED-A72E-9661C3BBE2D8}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{5B8667E9-0CB6-4562-B2BD-B5E7768BC1A4} = {9B2D4167-1CC7-4D8B-A01C-E9919E809A0A}
+		{03DBB240-C4DD-4FF4-9405-F50CBF0A960D} = {9B2D4167-1CC7-4D8B-A01C-E9919E809A0A}
+		{7CBFAE62-0D0D-4075-A426-99945DD4E9A8} = {9B2D4167-1CC7-4D8B-A01C-E9919E809A0A}
+		{04455C36-F127-4D40-BCB3-78E7DE21E70E} = {9B2D4167-1CC7-4D8B-A01C-E9919E809A0A}
+		{75AF82A4-AF0E-425F-8CF5-62F4F54380B9} = {9B2D4167-1CC7-4D8B-A01C-E9919E809A0A}
+		{F910A8EA-9B0F-4BDA-87D4-0765EE973421} = {9B2D4167-1CC7-4D8B-A01C-E9919E809A0A}
+		{DB7FCF02-C949-4545-B58C-F514E85AB1FA} = {6A9515FC-D4B2-4221-8E74-78E7AFF0E421}
+		{9FD0BECC-AC5E-46A8-9749-46CD82BA89DE} = {9B2D4167-1CC7-4D8B-A01C-E9919E809A0A}
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {E96603EA-8E1D-4AA9-A474-D267A116C316}
+	EndGlobalSection
+EndGlobal
diff --git a/PerfDataExtensions/PerfDataExtensions.csproj b/PerfDataExtensions/PerfDataExtensions.csproj
index 5837844..3d4a743 100644
--- a/PerfDataExtensions/PerfDataExtensions.csproj
+++ b/PerfDataExtensions/PerfDataExtensions.csproj
@@ -61,7 +61,7 @@
   
 
   
-    
+    
       compile
     
     
diff --git a/PerfDataExtensions/Properties/launchSettings.json b/PerfDataExtensions/Properties/launchSettings.json
index a29d98f..552a517 100644
--- a/PerfDataExtensions/Properties/launchSettings.json
+++ b/PerfDataExtensions/Properties/launchSettings.json
@@ -1,9 +1,9 @@
-{
-  "profiles": {
-    "PerfDataExtensions": {
-      "commandName": "Executable",
-      "executablePath": "C:\\Tools\\WPT\\latest\\wpa.exe",
-      "commandLineArgs": "-addsearchdir C:\\src\\Microsoft-Performance-Tools-Linux\\PerfDataExtensions\\bin\\Debug\\netstandard2.1"
-    }
-  }
+{
+  "profiles": {
+    "PerfDataExtensions": {
+      "commandName": "Executable",
+      "executablePath": "C:\\Tools\\WPT\\latest\\wpa.exe",
+      "commandLineArgs": "-addsearchdir E:\\src\\Microsoft-Performance-Tools-Linux-Android\\PerfDataExtensions\\bin\\Debug"
+    }
+  }
 }
\ No newline at end of file
diff --git a/PerfDataExtensions/SourceDataCookers/PerfDataProcessingSource.cs b/PerfDataExtensions/SourceDataCookers/PerfDataProcessingSource.cs
index da166b3..c1818b7 100644
--- a/PerfDataExtensions/SourceDataCookers/PerfDataProcessingSource.cs
+++ b/PerfDataExtensions/SourceDataCookers/PerfDataProcessingSource.cs
@@ -9,18 +9,6 @@
 
 namespace PerfDataProcessingSource
 {
-    //
-    // This is a sample Custom Data Source (CDS) that understands files with the .txt extension
-    //
-
-    // In order for a CDS to be recognized, it MUST satisfy the following:
-    //  a) Be a public type
-    //  b) Have a public parameterless constructor
-    //  c) Implement the IProcessingSource interface
-    //  d) Be decorated with the ProcessingSourceAttribute attribute
-    //  e) Be decorated with at least one of the derivatives of the DataSourceAttribute attribute
-    //
-
     [ProcessingSource(
         "{EA48A279-2B4E-43A0-AC86-030113A23064}",   // The GUID must be unique for your Custom Data Source. You can use Visual Studio's Tools -> Create Guid… tool to create a new GUID
         "Linux Perf Txt Data",                               // The Custom Data Source MUST have a name
@@ -29,13 +17,6 @@ namespace PerfDataProcessingSource
         ".txt",                                              // A file extension is REQUIRED
         "Linux perf.data.txt parser")]  // A description is OPTIONAL. The description is what appears in the file open menu to help users understand what the file type actually is. 
 
-    //
-    // There are two methods to creating a Custom Data Source that is recognized by UI:
-    //    1. Using the helper abstract base classes
-    //    2. Implementing the raw interfaces
-    // This sample demonstrates method 1 where the ProcessingSource abstract class
-    // helps provide a public parameterless constructor and implement the IProcessingSource interface
-    //
 
     public class PerfDataProcessingSource
         : ProcessingSource
@@ -69,12 +50,6 @@ protected override ICustomDataProcessor CreateProcessorCore(
             IProcessorEnvironment processorEnvironment,
             ProcessorOptions options)
         {
-            //
-            // Create a new instance implementing ICustomDataProcessor here to process the specified data sources.
-            // Note that you can have more advanced logic here to create different processors if you would like based on the file, or any other criteria.
-            // You are not restricted to always returning the same type from this method.
-            //
-
             return new PerfDataCustomDataProcessor(
                 dataSources.Select(x => x.Uri.LocalPath).ToArray(),
                 options,
diff --git a/PerfDataExtensions/Tables/CpuSamplingTable.cs b/PerfDataExtensions/Tables/CpuSamplingTable.cs
index ab6bd76..9c02b92 100644
--- a/PerfDataExtensions/Tables/CpuSamplingTable.cs
+++ b/PerfDataExtensions/Tables/CpuSamplingTable.cs
@@ -9,10 +9,10 @@
 using PerfCds.CookerData;
 using PerfDataExtensions.DataOutputTypes;
 using PerfDataExtensions.SourceDataCookers.Cpu;
-using PerfDataExtensions.Tables.Generators;
 using Utilities.AccessProviders;
-using static PerfDataExtensions.Tables.TimeHelper;
-
+using Utilities.Generators;
+using static Utilities.TimeHelper;
+
 namespace PerfDataExtensions.Tables
 {
     [Table]
diff --git a/PerfDataExtensions/Tables/PerfTxtCpuSamplingTable.cs b/PerfDataExtensions/Tables/PerfTxtCpuSamplingTable.cs
index a46888d..a78beb4 100644
--- a/PerfDataExtensions/Tables/PerfTxtCpuSamplingTable.cs
+++ b/PerfDataExtensions/Tables/PerfTxtCpuSamplingTable.cs
@@ -10,10 +10,10 @@
 using Microsoft.Diagnostics.Tracing.StackSources;
 using Microsoft.Performance.SDK;
 using Microsoft.Performance.SDK.Processing;
-using PerfDataExtensions.Tables.Generators;
 using Utilities.AccessProviders;
-using static PerfDataExtensions.Tables.TimeHelper;
-
+using Utilities.Generators;
+using static Utilities.TimeHelper;
+
 namespace PerfDataExtensions.Tables
 {
     //
diff --git a/PerfUnitTest/PerfUnitTest.csproj b/PerfUnitTest/PerfUnitTest.csproj
index b68c7b2..c58e4af 100644
--- a/PerfUnitTest/PerfUnitTest.csproj
+++ b/PerfUnitTest/PerfUnitTest.csproj
@@ -7,13 +7,13 @@
   
 
   
-    
-    
+    
+    
     
     
-    
-    
-    
+    
+    
+    
     
       all
       runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/PerfettoUnitTest/PerfettoUnitTest.csproj b/PerfettoUnitTest/PerfettoUnitTest.csproj
index d128e01..b457e66 100644
--- a/PerfettoUnitTest/PerfettoUnitTest.csproj
+++ b/PerfettoUnitTest/PerfettoUnitTest.csproj
@@ -7,13 +7,13 @@
   
 
   
-    
+    
     
     
     
-    
-    
-    
+    
+    
+    
     
       all
       runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/README.md b/README.md
index 2ee856c..a7377c9 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,7 @@
 - [LTTng](https://lttng.org) (Kernel CPU scheduling, Processes, Threads, Block IO/Disk, Syscalls, File events, etc)
 - [perf](https://perf.wiki.kernel.org/) CPU Sampling(cpu-clock)
 - [Perfetto](https://perfetto.dev/) Android & Chromium (CPU Scheduling, CPU Sampling, CPU Frequency, FTrace, Android Logs, Generic Events / Default Tracks, GPU Counters, Jank Detection, Processes, Android Packages)
+- [dotnet-trace (.nettrace)](https://docs.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-trace) - Cross-platform .NET trace. CPU Sampling, Generic Events, Exceptions, Garbage Collector (GC)
 
 > Logs supported: 
 - [Dmesg](https://en.wikipedia.org/wiki/Dmesg)
@@ -42,6 +43,8 @@ Featuring some use-cases and some walkthroughs
 - [.NET Core SDK 3.1.x](https://dotnet.microsoft.com/download/dotnet-core/3.1)
 - [Visual Studio](https://visualstudio.microsoft.com/), [VSCode](https://visualstudio.microsoft.com/), or your favorite editor!
 
+See [full developer instructions](DeveloperInfo.md) for more information
+
 # Download
 - **For plugins Download** see [Releases](https://github.com/microsoft/Microsoft-Performance-Tools-Linux/releases)
 
@@ -85,6 +88,8 @@ The tools can be run in several modes:
         wpa.exe /?
         "C:\Program Files\WindowsApps\Microsoft.WindowsPerformanceAnalyzerPreview_10.0.22504.0_x64__8wekyb3d8bbwe\10\Windows Performance Toolkit\wpa.exe" /?
         ```
+        PowerShell:
+        ```Get-AppxPackage Microsoft.WindowsPerformanceAnalyzer*```
     - Verify that these 2 command line WPA options are supported:
       - OPTIONS: **-addsearchdir PATH**. Adds a directory path to the plugin search path. ....
       - ENVIRONMENT VARIABLES: **WPA_ADDITIONAL_SEARCH_DIRECTORIES** - A semicolon (;) delimited list of additional directories to search for plugins. Equivalent to the -addsearchdir option.
@@ -96,6 +101,7 @@ The tools can be run in several modes:
 # How to capture a trace or logs
 - Linux 
   - Please see [Linux Trace Log Capture](LinuxTraceLogCapture.md)
+- Cross Platform [dotnet-trace (.nettrace)](https://docs.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-trace)
 - Perfetto
   - Android - Please see [Record traces on Android](https://perfetto.dev/docs/quickstart/android-tracing)
   - Linux - Please see [Record traces on Linux](https://perfetto.dev/docs/quickstart/linux-tracing)
@@ -109,13 +115,12 @@ The tools can be run in several modes:
     - Note: Requires >= 1.2 release AND WPA >= 10.6.20.1 (via WPA Help -> About)
 - Perfetto
   - WPA -> Open -> (Select Perfetto trace file)
-    - Note: The Perfetto plugin explicitly supports the _.perfetto-trace_ and _.pftrace_ file types, but it does support more (e.g. Protobuf, Chrome JSON). You just need to rename to one of the stated supported types
-- Unified (LTTng, Perfetto, or other multiple different logs files together)
-  - Once you gather the data, there is a tiny bit of prep needed to open them in a single unified timeline (like the screenshot above)
+- Single session (LTTng, Perfetto, or other multiple different logs files together)
+  - Once you gather the data, there is a tiny bit of prep needed to open them in a single session (unified timeline) (like the screenshot above)
   - If you want to open multiple logs together in single timeline - Copy all trace files and logs you want to open to single folder
   - Example: You want to open in the same timeline: LTTng, Perf CPU Sampling, Dmesg
     - Ensure that the Linux CTF folder/trace is zipped and renamed to .ctf in the same folder (hack so open Unified works)
-  - WPA -> File -> Open -> Multi-select all files and choose "Open Unified"
+  - WPA -> File -> Open -> Multi-select all files and choose "Single Session"
 
 # How do I use WPA in general?
 If you want to learn how to use the GUI UI in general see [WPA MSDN Docs](https://docs.microsoft.com/en-us/windows-hardware/test/wpt/windows-performance-analyzer)
diff --git a/TestData/Dotnet-Trace/HelloWorld_GC_Threads_Exception.nettrace b/TestData/Dotnet-Trace/HelloWorld_GC_Threads_Exception.nettrace
new file mode 100644
index 0000000..776cfa8
Binary files /dev/null and b/TestData/Dotnet-Trace/HelloWorld_GC_Threads_Exception.nettrace differ
diff --git a/UnitTestCommon/ITableResultExts.cs b/UnitTestCommon/ITableResultExts.cs
index 2680dd3..6bce3c9 100644
--- a/UnitTestCommon/ITableResultExts.cs
+++ b/UnitTestCommon/ITableResultExts.cs
@@ -1,18 +1,29 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
 
+using Microsoft.Performance.SDK.Processing;
 using Microsoft.Performance.Toolkit.Engine;
 using System;
+using System.Collections.Generic;
 using System.Linq;
 
 namespace UnitTestCommon
 {
+    public class ColumnsWithData
+    {
+        public IEnumerable ColumnNames{ get; set; }
+        public object[][] Data { get; set; }
+    }
+
     public static class ITableResultExts
     {
-        public static object[][] GetDataForAllRows(this ITableResult tableResult)
+        public static ColumnsWithData GetDataForAllRows(this ITableResult tableResult)
         {
-            var allColumns = tableResult.Columns.Select(f => f.Configuration.Metadata.Guid).ToArray();
-            return GetDataForAllRows(tableResult, allColumns);
+            var allColumns = tableResult.Columns.Select(f => f.Configuration.Metadata);
+            var columnsWithData = new ColumnsWithData();
+            columnsWithData.ColumnNames = allColumns.Select(f => f.Name).ToArray();
+            columnsWithData.Data = GetDataForAllRows(tableResult, allColumns.Select(f => f.Guid).ToArray());
+            return columnsWithData;
         }
 
         public static object[][] GetDataForAllRows(this ITableResult tableResult, Guid[] columns)
diff --git a/UnitTestCommon/UnitTest.cs b/UnitTestCommon/UnitTest.cs
index 9e83816..c41d6a3 100644
--- a/UnitTestCommon/UnitTest.cs
+++ b/UnitTestCommon/UnitTest.cs
@@ -19,9 +19,12 @@ public static void TestTableBuild(RuntimeExecutionResults runtimeExecutionResult
             if (skipDataAvailableCheck || isTableDataAvailable)
             {
                 var tableResult = runtimeExecutionResults.BuildTable(tableDescriptor);
-                Assert.IsTrue(tableResult.RowCount == expectedCount);
+                if (tableResult.RowCount != expectedCount)
+                {
+                    throw new Exception($"We have {tableResult.RowCount} rows for {tableDescriptor.Name}, but we expected {expectedCount} rows");
+                }
                 var tableData = tableResult.GetDataForAllRows();
-                Assert.IsTrue(tableData.Length == expectedCount);
+                Assert.IsTrue(tableData.Data.Length == expectedCount);
             }
             else if (!isTableDataAvailable && expectedCount > 0)
             {
diff --git a/UnitTestCommon/UnitTestCommon.csproj b/UnitTestCommon/UnitTestCommon.csproj
index 7233b27..d0462dd 100644
--- a/UnitTestCommon/UnitTestCommon.csproj
+++ b/UnitTestCommon/UnitTestCommon.csproj
@@ -7,7 +7,7 @@
   
     
     
-    
+    
   
 
 
diff --git a/PerfDataExtensions/Tables/Generators/IMapListToStream.cs b/Utilities/Generators/IMapListToStream.cs
similarity index 90%
rename from PerfDataExtensions/Tables/Generators/IMapListToStream.cs
rename to Utilities/Generators/IMapListToStream.cs
index a0224d5..113e188 100644
--- a/PerfDataExtensions/Tables/Generators/IMapListToStream.cs
+++ b/Utilities/Generators/IMapListToStream.cs
@@ -1,7 +1,7 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
 
-namespace PerfDataExtensions.Tables.Generators
+namespace Utilities.Generators
 {
     public interface IMapListToStream
     {
diff --git a/PerfDataExtensions/Tables/Generators/SequentialGenerator.cs b/Utilities/Generators/SequentialGenerator.cs
similarity index 99%
rename from PerfDataExtensions/Tables/Generators/SequentialGenerator.cs
rename to Utilities/Generators/SequentialGenerator.cs
index 361187b..7587eff 100644
--- a/PerfDataExtensions/Tables/Generators/SequentialGenerator.cs
+++ b/Utilities/Generators/SequentialGenerator.cs
@@ -6,7 +6,7 @@
 using Microsoft.Performance.SDK;
 using Microsoft.Performance.SDK.Processing;
 
-namespace PerfDataExtensions.Tables.Generators
+namespace Utilities.Generators
 {
     public static class SequentialGenerator
     {
diff --git a/PerfDataExtensions/Tables/TimeHelper.cs b/Utilities/TimeHelper.cs
similarity index 95%
rename from PerfDataExtensions/Tables/TimeHelper.cs
rename to Utilities/TimeHelper.cs
index 174dd1f..9edc207 100644
--- a/PerfDataExtensions/Tables/TimeHelper.cs
+++ b/Utilities/TimeHelper.cs
@@ -3,7 +3,7 @@
 
 using Microsoft.Performance.SDK;
 
-namespace PerfDataExtensions.Tables
+namespace Utilities
 {
     public static class TimeHelper
     {
diff --git a/Utilities/Utilities.csproj b/Utilities/Utilities.csproj
index 7ad8b60..aa4c95c 100644
--- a/Utilities/Utilities.csproj
+++ b/Utilities/Utilities.csproj
@@ -2,7 +2,7 @@
 
   
     netstandard2.1
-	1.2.2
+	1.2.3
     true
     Microsoft
     Microsoft Corp.
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 5d73c91..f9d7048 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -52,6 +52,13 @@ jobs:
         projects:  |
           CtfPlayback\CtfPlayback.csproj
           CtfUnitTest\CtfUnitTest.csproj
+          DotNetEventPipe\DotnetEventpipe.csproj
+          DotnetEventpipeTest\DotnetEventpipeTest.csproj
+          LinuxLogParsers\LinuxPlugins-MicrosoftPerformanceToolkSDK\Cloud-init\Cloud-Init.csproj
+          LinuxLogParsers\LinuxPlugins-MicrosoftPerformanceToolkSDK\DmesgIsoLog\Dmesg.csproj
+          LinuxLogParsers\LinuxPlugins-MicrosoftPerformanceToolkSDK\WaLinuxAgent\WaLinuxAgent.csproj
+          LinuxLogParsers\LinuxPlugins-MicrosoftPerformanceToolkSDK\AndroidLogCat\AndroidLogcat.csproj
+          LinuxLogParsers\LinuxLogParsersUnitTest\LinuxLogParsersUnitTest.csproj
           LTTngCds\LTTngCds.csproj
           LTTngDataExtensions\LTTngDataExtensions.csproj
           LTTngDataExtUnitTest\LTTngDataExtUnitTest.csproj
@@ -60,11 +67,6 @@ jobs:
           PerfDataExtensions\PerfDataExtensions.csproj
           PerfettoCds\PerfettoCds.csproj
           PerfUnitTest\PerfUnitTest.csproj
-          LinuxLogParsers\LinuxPlugins-MicrosoftPerformanceToolkSDK\Cloud-init\Cloud-Init.csproj
-          LinuxLogParsers\LinuxPlugins-MicrosoftPerformanceToolkSDK\DmesgIsoLog\Dmesg.csproj
-          LinuxLogParsers\LinuxPlugins-MicrosoftPerformanceToolkSDK\WaLinuxAgent\WaLinuxAgent.csproj
-          LinuxLogParsers\LinuxPlugins-MicrosoftPerformanceToolkSDK\AndroidLogCat\AndroidLogcat.csproj
-          LinuxLogParsers\LinuxLogParsersUnitTest\LinuxLogParsersUnitTest.csproj
           
         includesymbols: true
         versioningScheme: 'byBuildNumber'
@@ -131,6 +133,13 @@ jobs:
         Contents: '**'
         TargetFolder: '$(Build.ArtifactStagingDirectory)/Microsoft-Performance-Tools-Linux/MicrosoftPerfToolkitAddins/AndroidLogCat'
         
+    - task: CopyFiles@2
+      displayName: Copy DotNetEventPipe Build to Output Artifacts
+      inputs:
+        SourceFolder: 'DotNetEventPipe/bin/$(BuildConfiguration)/netstandard2.1'
+        Contents: '**'
+        TargetFolder: '$(Build.ArtifactStagingDirectory)/Microsoft-Performance-Tools-Linux/MicrosoftPerfToolkitAddins/DotNetEventPipe'
+
     - task: CopyFiles@2
       displayName: Copy Launcher
       inputs: