Make dotnet-counters use IPC to enable/disable counters (#148)
authorSung Yoon Whang <suwhang@microsoft.com>
Fri, 29 Mar 2019 07:03:08 +0000 (00:03 -0700)
committerGitHub <noreply@github.com>
Fri, 29 Mar 2019 07:03:08 +0000 (00:03 -0700)
* Modify dotnet-counters to use IPC to enable/disable counters

* Add parsing for counterList command line arg

* remove unused using

* move scope of defaultProvider

* Update RuntimeClient namespace

src/Tools/dotnet-counters/CollectionConfiguration.cs [deleted file]
src/Tools/dotnet-counters/ConfigPathDetector.cs [deleted file]
src/Tools/dotnet-counters/CounterMonitor.cs
src/Tools/dotnet-counters/CounterProvider.cs
src/Tools/dotnet-counters/EventCollector.cs [deleted file]
src/Tools/dotnet-counters/EventPipeCollector.cs [deleted file]
src/Tools/dotnet-counters/StringParser.cs [deleted file]
src/Tools/dotnet-counters/dotnet-counters.csproj

diff --git a/src/Tools/dotnet-counters/CollectionConfiguration.cs b/src/Tools/dotnet-counters/CollectionConfiguration.cs
deleted file mode 100644 (file)
index c1776ca..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.Tracing;
-using System.Linq;
-using System.Text;
-
-namespace Microsoft.Diagnostics.Tools.Counters
-{
-    public class CollectionConfiguration
-    {
-        public int? ProcessId { get; set; }
-        public string OutputPath { get; set; }
-        public int? CircularMB { get; set; }
-        public int? Interval { get; set; }
-        public IList<CounterProvider> Providers { get; set; } = new List<CounterProvider>();
-
-        internal string ToConfigString()
-        {
-            var builder = new StringBuilder();
-            if (ProcessId != null)
-            {
-                builder.AppendLine($"ProcessId={ProcessId.Value}");
-            }
-            if (!string.IsNullOrEmpty(OutputPath))
-            {
-                builder.AppendLine($"OutputPath={OutputPath}");
-            }
-            if (CircularMB != null)
-            {
-                builder.AppendLine($"CircularMB={CircularMB}");
-            }
-            if (Providers != null && Providers.Count > 0)
-            {
-                builder.AppendLine($"Providers={SerializeProviders(Providers)}");
-            }
-            return builder.ToString();
-        }
-
-        public void AddProvider(CounterProvider provider)
-        {
-            Providers.Add(provider);
-        }
-
-        private string SerializeProviders(IEnumerable<CounterProvider> providers) => string.Join(",", providers.Select(p => SerializeProvider(p)));
-
-        private string SerializeProvider(CounterProvider provider)
-        {
-            return $"{provider.Name}:{provider.Keywords}:{provider.Level}:EventCounterIntervalSec={Interval}";
-        }
-
-    }
-}
diff --git a/src/Tools/dotnet-counters/ConfigPathDetector.cs b/src/Tools/dotnet-counters/ConfigPathDetector.cs
deleted file mode 100644 (file)
index abe7026..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices;
-
-namespace Microsoft.Diagnostics.Tools.Counters
-{
-    // NOTE: This is copied over from dotnet-collect.
-    // This will eventually go away once we get the runtime ready to stream events via IPC.
-    internal static class ConfigPathDetector
-    {
-        private static readonly HashSet<string> _managedExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".exe", ".dll" };
-
-        // Known .NET Platform Assemblies
-        private static readonly HashSet<string> _platformAssemblies = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
-        {
-            "System.Private.CoreLib.dll",
-            "clrjit.dll",
-        };
-
-        internal static string TryDetectConfigPath(int processId)
-        {
-            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
-            {
-                return Windows.TryDetectConfigPath(processId);
-            }
-            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
-            {
-                return Linux.TryDetectConfigPath(processId);
-            }
-            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
-            {
-                return OSX.TryDetectConfigPath(processId);
-            }
-            return null;
-        }
-
-        private static class OSX
-        {
-            // This is defined in proc_info.h (https://opensource.apple.com/source/xnu/xnu-1228/bsd/sys/proc_info.h)
-            private const int PROC_PIDPATHINFO_MAXSIZE = 1024 * 4;
-
-            /// <summary>
-            /// Gets the full path to the executable file identified by the specified PID
-            /// </summary>
-            /// <param name="pid">The PID of the running process</param>
-            /// <param name="buffer">A pointer to an allocated block of memory that will be filled with the process path</param>
-            /// <param name="bufferSize">The size of the buffer, should be PROC_PIDPATHINFO_MAXSIZE</param>
-            /// <returns>Returns the length of the path returned on success</returns>
-            [DllImport("libproc.dylib", SetLastError = true)]
-            private static extern unsafe int proc_pidpath(
-                int pid, 
-                byte* buffer, 
-                uint bufferSize);
-
-            /// <summary>
-            /// Gets the full path to the executable file identified by the specified PID
-            /// </summary>
-            /// <param name="pid">The PID of the running process</param>
-            /// <returns>Returns the full path to the process executable</returns>
-            internal static unsafe string proc_pidpath(int pid)
-            {
-                // The path is a fixed buffer size, so use that and trim it after
-                int result = 0;
-                byte* pBuffer = stackalloc byte[PROC_PIDPATHINFO_MAXSIZE];
-
-                // WARNING - Despite its name, don't try to pass in a smaller size than specified by PROC_PIDPATHINFO_MAXSIZE.
-                // For some reason libproc returns -1 if you specify something that's NOT EQUAL to PROC_PIDPATHINFO_MAXSIZE
-                // even if you declare your buffer to be smaller/larger than this size. 
-                result = proc_pidpath(pid, pBuffer, (uint)(PROC_PIDPATHINFO_MAXSIZE * sizeof(byte)));
-                if (result <= 0)
-                {
-                    throw new InvalidOperationException("Could not find procpath using libproc.");
-                }
-
-                // OS X uses UTF-8. The conversion may not strip off all trailing \0s so remove them here
-                return System.Text.Encoding.UTF8.GetString(pBuffer, result);
-            }
-
-            public static string TryDetectConfigPath(int processId)
-            {
-                try
-                {
-                    var path = proc_pidpath(processId);
-                    var candidateDir = Path.GetDirectoryName(path);
-                    var candidateName = Path.GetFileNameWithoutExtension(path);
-                    return Path.Combine(candidateDir, $"{candidateName}.eventpipeconfig");
-                }
-                catch (InvalidOperationException)
-                {
-                    return null;  // The pinvoke above may fail - return null in that case to handle error gracefully.
-                }
-            }
-        }
-
-        private static class Linux
-        {
-            public static string TryDetectConfigPath(int processId)
-            {
-                // Read procfs maps list
-                var lines = File.ReadAllLines($"/proc/{processId}/maps");
-
-                foreach (var line in lines)
-                {
-                    try
-                    {
-                        var parser = new StringParser(line, separator: ' ', skipEmpty: true);
-
-                        // Skip the address range
-                        parser.MoveNext();
-
-                        var permissions = parser.MoveAndExtractNext();
-
-                        // The managed entry point is Read-Only, Non-Execute and Shared.
-                        if (!string.Equals(permissions, "r--s", StringComparison.Ordinal))
-                        {
-                            continue;
-                        }
-
-                        // Skip offset, dev, and inode
-                        parser.MoveNext();
-                        parser.MoveNext();
-                        parser.MoveNext();
-
-                        // Parse the path
-                        if (!parser.MoveNext())
-                        {
-                            continue;
-                        }
-
-                        var path = parser.ExtractCurrentToEnd();
-                        var candidateDir = Path.GetDirectoryName(path);
-                        var candidateName = Path.GetFileNameWithoutExtension(path);
-                        if (File.Exists(Path.Combine(candidateDir, $"{candidateName}.deps.json")))
-                        {
-                            return Path.Combine(candidateDir, $"{candidateName}.eventpipeconfig");
-                        }
-                    }
-                    catch (ArgumentNullException) { return null; }
-                    catch (InvalidDataException) { return null; }
-                    catch (InvalidOperationException) { return null; }
-                }
-                return null;
-            }
-        }
-
-        private static class Windows
-        {
-            private static readonly HashSet<string> _knownNativeLibraries = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
-            {
-                // .NET Core Host
-                "dotnet.exe",
-                "hostfxr.dll",
-                "hostpolicy.dll",
-                "coreclr.dll",
-
-                // Windows Native Libraries
-                "ntdll.dll",
-                "kernel32.dll",
-                "kernelbase.dll",
-                "apphelp.dll",
-                "ucrtbase.dll",
-                "advapi32.dll",
-                "msvcrt.dll",
-                "sechost.dll",
-                "rpcrt4.dll",
-                "ole32.dll",
-                "combase.dll",
-                "bcryptPrimitives.dll",
-                "gdi32.dll",
-                "gdi32full.dll",
-                "msvcp_win.dll",
-                "user32.dll",
-                "win32u.dll",
-                "oleaut32.dll",
-                "shlwapi.dll",
-                "version.dll",
-                "bcrypt.dll",
-                "imm32.dll",
-                "kernel.appcore.dll",
-            };
-
-            public static string TryDetectConfigPath(int processId)
-            {
-                try
-                {
-
-                    var process = Process.GetProcessById(processId);
-
-                    // Iterate over modules
-                    foreach (var module in process.Modules.Cast<ProcessModule>())
-                    {
-                        // Filter out things that aren't exes and dlls (useful on Unix/macOS to skip native libraries)
-                        var extension = Path.GetExtension(module.FileName);
-                        var name = Path.GetFileName(module.FileName);
-                        if (_managedExtensions.Contains(extension) && !_knownNativeLibraries.Contains(name) && !_platformAssemblies.Contains(name))
-                        {
-                            var candidateDir = Path.GetDirectoryName(module.FileName);
-                            var appName = Path.GetFileNameWithoutExtension(module.FileName);
-
-                            // Check for the deps.json file
-                            // TODO: Self-contained apps?
-                            if (File.Exists(Path.Combine(candidateDir, $"{appName}.deps.json")))
-                            {
-                                // This is an app!
-                                return Path.Combine(candidateDir, $"{appName}.eventpipeconfig");
-                            }
-                        }
-                    }
-                }
-                catch (ArgumentException)
-                {
-                    return null;
-                }
-
-                return null;
-            }
-        }
-    }
-}
index c718a3899a2387ed560dc7aa1de23f266a7d2563..66cd4c030cd2b94ee0dbaa6cdf7503169c1c99fc 100644 (file)
@@ -3,20 +3,23 @@
 
 using System;
 using System.CommandLine;
-using System.Diagnostics;
+using System.Collections.Generic;
 using System.IO;
+using System.Linq;
+using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using System.CommandLine.Builder;
 using System.CommandLine.Invocation;
 
+using Microsoft.Diagnostics.Tools.RuntimeClient;
+
 namespace Microsoft.Diagnostics.Tools.Counters
 {
     public class CounterMonitor
     {
-        private string configPath;
-        private EventPipeCollector collector;
-        private CollectionConfiguration config;
+        private string outputPath;
+        private ulong sessionId;
 
         private int _processId;
         private int _interval;
@@ -43,13 +46,26 @@ namespace Microsoft.Diagnostics.Tools.Counters
 
             catch (OperationCanceledException)
             {
-                await collector.StopCollectingAsync();
-                console.Out.WriteLine($"Tracing stopped. Trace files written to {config.OutputPath}");
+                try
+                {
+                    EventPipeClient.DisableTracingToFile(_processId, sessionId);    
+                }
+                catch (Exception) {} // Swallow all exceptions for now.
+                
+                console.Out.WriteLine($"Tracing stopped. Trace files written to {outputPath}");
                 console.Out.WriteLine($"Complete");
                 return 1;
             }
         }
 
+        private static IEnumerable<Provider> ToProviders(string providers)
+        {
+            if (string.IsNullOrWhiteSpace(providers))
+                throw new ArgumentNullException(nameof(providers));
+            return providers.Split(',')
+                .Select(Provider.ToProvider);
+        }
+
         private async Task<int> StartMonitor()
         {
             if (_processId == 0) {
@@ -62,47 +78,53 @@ namespace Microsoft.Diagnostics.Tools.Counters
                 return 1;
             }
 
-            configPath = ConfigPathDetector.TryDetectConfigPath(_processId);
+            outputPath = Path.Combine(Directory.GetCurrentDirectory(), $"dotnet-counters-{_processId}.netperf"); // TODO: This can be removed once events can be streamed in real time.
 
-            if(string.IsNullOrEmpty(configPath))
-            {
-                _console.Error.WriteLine("Couldn't determine the path for the eventpipeconfig file from the process ID.");
-                return 1;
-            }
-
-            _console.Out.WriteLine($"Detected config file path: {configPath}");
-
-            config = new CollectionConfiguration()
-            {
-                ProcessId = _processId,
-                CircularMB = 1000,  // TODO: Make this configurable?
-                OutputPath = Directory.GetCurrentDirectory(),
-                Interval = _interval
-            };
+            String providerString;
 
             if (string.IsNullOrEmpty(_counterList))
             {
+                CounterProvider defaultProvider = null;
                 _console.Out.WriteLine($"counter_list is unspecified. Monitoring all counters by default.");
 
                 // Enable the default profile if nothing is specified
-                if (!KnownData.TryGetProvider("System.Runtime", out var defaultProvider))
+                if (!KnownData.TryGetProvider("System.Runtime", out defaultProvider))
                 {
                     _console.Error.WriteLine("No providers or profiles were specified and there is no default profile available.");
                     return 1;
                 }
-                config.AddProvider(defaultProvider);
+                providerString = defaultProvider.ToProviderString(_interval);
             }
-
-            if (File.Exists(configPath))
+            else
             {
-                _console.Error.WriteLine("Config file already exists, tracing is already underway by a different consumer.");
-                return 1;
+                string[] counters = _counterList.Split(" ");
+                CounterProvider provider = null;
+                StringBuilder sb = new StringBuilder("");
+                for (var i = 0; i < counters.Length; i++)
+                {
+                    if (!KnownData.TryGetProvider(counters[i], out provider))
+                    {
+                        _console.Error.WriteLine($"No known provider called {counters[i]}.");
+                        return 1;
+                    }
+                    sb.Append(provider.ToProviderString(_interval));
+                    if (i != counters.Length - 1)
+                    {
+                        sb.Append(",");
+                    }
+                }
+                providerString = sb.ToString();
             }
 
-            collector = new EventPipeCollector(config, configPath);
+            var configuration = new SessionConfiguration(
+                1000,
+                0,
+                outputPath,
+                ToProviders(providerString));
+
+            sessionId = EventPipeClient.EnableTracingToFile(_processId, configuration);
 
             // Write the config file contents
-            await collector.StartCollectingAsync();
             _console.Out.WriteLine("Tracing has started. Press Ctrl-C to stop.");
             await Task.Delay(int.MaxValue, _ct);
             return 0;
index 84cccdca68eba3fba8e4921f6d949d562fcb788b..5c6e62835ef2de917eba4b4b255f27374ebf3d75 100644 (file)
@@ -24,6 +24,11 @@ namespace Microsoft.Diagnostics.Tools.Counters
             Level = level;
             Counters = counters.ToList();
         }
+
+        public string ToProviderString(int interval)
+        {
+            return $"{Name}:{Keywords}:{Level}:EventCounterIntervalSec={interval}";
+        }
     }
 
     public class CounterProfile
diff --git a/src/Tools/dotnet-counters/EventCollector.cs b/src/Tools/dotnet-counters/EventCollector.cs
deleted file mode 100644 (file)
index f81327d..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Threading.Tasks;
-
-namespace Microsoft.Diagnostics.Tools.Counters
-{
-    public abstract class EventCollector
-    {
-        public abstract Task StartCollectingAsync();
-        public abstract Task StopCollectingAsync();
-    }
-}
diff --git a/src/Tools/dotnet-counters/EventPipeCollector.cs b/src/Tools/dotnet-counters/EventPipeCollector.cs
deleted file mode 100644 (file)
index 255b62c..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-using System.IO;
-using System.Threading.Tasks;
-
-namespace Microsoft.Diagnostics.Tools.Counters
-{
-    public class EventPipeCollector : EventCollector
-    {
-        private readonly CollectionConfiguration _config;
-        private readonly string _configPath;
-
-        public EventPipeCollector(CollectionConfiguration config, string configPath)
-        {
-            _config = config;
-            _configPath = configPath;
-        }
-
-        public override Task StartCollectingAsync()
-        {
-            var configContent = _config.ToConfigString();
-            return File.WriteAllTextAsync(_configPath, configContent);
-        }
-
-        public override Task StopCollectingAsync()
-        {
-            File.Delete(_configPath);
-            return Task.CompletedTask;
-        }
-    }
-}
diff --git a/src/Tools/dotnet-counters/StringParser.cs b/src/Tools/dotnet-counters/StringParser.cs
deleted file mode 100644 (file)
index 5c3c643..0000000
+++ /dev/null
@@ -1,334 +0,0 @@
-// Borrowed from https://github.com/dotnet/corefx/blob/b2f960abe1d8690be9d68dd9b56ea7636fb4a38b/src/Common/src/System/IO/StringParser.cs
-
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-
-namespace System.IO
-{
-    /// <summary>
-    /// Provides a string parser that may be used instead of String.Split 
-    /// to avoid unnecessary string and array allocations.
-    /// </summary>
-    internal struct StringParser
-    {
-        /// <summary>The string being parsed.</summary>
-        private readonly string _buffer;
-
-        /// <summary>The separator character used to separate subcomponents of the larger string.</summary>
-        private readonly char _separator;
-
-        /// <summary>true if empty subcomponents should be skipped; false to treat them as valid entries.</summary>
-        private readonly bool _skipEmpty;
-
-        /// <summary>The starting index from which to parse the current entry.</summary>
-        private int _startIndex;
-
-        /// <summary>The ending index that represents the next index after the last character that's part of the current entry.</summary>
-        private int _endIndex;
-
-        /// <summary>Initialize the StringParser.</summary>
-        /// <param name="buffer">The string to parse.</param>
-        /// <param name="separator">The separator character used to separate subcomponents of <paramref name="buffer"/>.</param>
-        /// <param name="skipEmpty">true if empty subcomponents should be skipped; false to treat them as valid entries.  Defaults to false.</param>
-        public StringParser(string buffer, char separator, bool skipEmpty = false)
-        {
-            if (buffer == null)
-            {
-                throw new ArgumentNullException(nameof(buffer));
-            }
-            _buffer = buffer;
-            _separator = separator;
-            _skipEmpty = skipEmpty;
-            _startIndex = -1;
-            _endIndex = -1;
-        }
-
-        /// <summary>Moves to the next component of the string.</summary>
-        /// <returns>true if there is a next component to be parsed; otherwise, false.</returns>
-        public bool MoveNext()
-        {
-            if (_buffer == null)
-            {
-                throw new InvalidOperationException();
-            }
-
-            while (true)
-            {
-                if (_endIndex >= _buffer.Length)
-                {
-                    _startIndex = _endIndex;
-                    return false;
-                }
-
-                int nextSeparator = _buffer.IndexOf(_separator, _endIndex + 1);
-                _startIndex = _endIndex + 1;
-                _endIndex = nextSeparator >= 0 ? nextSeparator : _buffer.Length;
-
-                if (!_skipEmpty || _endIndex >= _startIndex + 1)
-                {
-                    return true;
-                }
-            }
-        }
-
-        /// <summary>
-        /// Moves to the next component of the string.  If there isn't one, it throws an exception.
-        /// </summary>
-        public void MoveNextOrFail()
-        {
-            if (!MoveNext())
-            {
-                ThrowForInvalidData();
-            }
-        }
-
-        /// <summary>
-        /// Moves to the next component of the string and returns it as a string.
-        /// </summary>
-        /// <returns></returns>
-        public string MoveAndExtractNext()
-        {
-            MoveNextOrFail();
-            return _buffer.Substring(_startIndex, _endIndex - _startIndex);
-        }
-
-        /// <summary>
-        /// Moves to the next component of the string, which must be enclosed in the only set of top-level parentheses
-        /// in the string.  The extracted value will be everything between (not including) those parentheses.
-        /// </summary>
-        /// <returns></returns>
-        public string MoveAndExtractNextInOuterParens()
-        {
-            // Move to the next position
-            MoveNextOrFail();
-
-            // After doing so, we should be sitting at a the opening paren.
-            if (_buffer[_startIndex] != '(')
-            {
-                ThrowForInvalidData();
-            }
-
-            // Since we only allow for one top-level set of parentheses, find the last
-            // parenthesis in the string; it's paired with the opening one we just found.
-            int lastParen = _buffer.LastIndexOf(')');
-            if (lastParen == -1 || lastParen < _startIndex)
-            {
-                ThrowForInvalidData();
-            }
-
-            // Extract the contents of the parens, then move our ending position to be after the paren
-            string result = _buffer.Substring(_startIndex + 1, lastParen - _startIndex - 1);
-            _endIndex = lastParen + 1;
-
-            return result;
-        }
-
-        /// <summary>
-        /// Gets the current subcomponent of the string as a string.
-        /// </summary>
-        public string ExtractCurrent()
-        {
-            if (_buffer == null || _startIndex == -1)
-            {
-                throw new InvalidOperationException();
-            }
-            return _buffer.Substring(_startIndex, _endIndex - _startIndex);
-        }
-
-        /// <summary>Moves to the next component and parses it as an Int32.</summary>
-        public unsafe int ParseNextInt32()
-        {
-            MoveNextOrFail();
-
-            bool negative = false;
-            int result = 0;
-
-            fixed (char* bufferPtr = _buffer)
-            {
-                char* p = bufferPtr + _startIndex;
-                char* end = bufferPtr + _endIndex;
-
-                if (p == end)
-                {
-                    ThrowForInvalidData();
-                }
-
-                if (*p == '-')
-                {
-                    negative = true;
-                    p++;
-                    if (p == end)
-                    {
-                        ThrowForInvalidData();
-                    }
-                }
-
-                while (p != end)
-                {
-                    int d = *p - '0';
-                    if (d < 0 || d > 9)
-                    {
-                        ThrowForInvalidData();
-                    }
-                    result = negative ? checked((result * 10) - d) : checked((result * 10) + d);
-
-                    p++;
-                }
-            }
-
-            Debug.Assert(result == int.Parse(ExtractCurrent()), "Expected manually parsed result to match Parse result");
-            return result;
-        }
-
-        /// <summary>Moves to the next component and parses it as an Int64.</summary>
-        public unsafe long ParseNextInt64()
-        {
-            MoveNextOrFail();
-
-            bool negative = false;
-            long result = 0;
-
-            fixed (char* bufferPtr = _buffer)
-            {
-                char* p = bufferPtr + _startIndex;
-                char* end = bufferPtr + _endIndex;
-
-                if (p == end)
-                {
-                    ThrowForInvalidData();
-                }
-
-                if (*p == '-')
-                {
-                    negative = true;
-                    p++;
-                    if (p == end)
-                    {
-                        ThrowForInvalidData();
-                    }
-                }
-
-                while (p != end)
-                {
-                    int d = *p - '0';
-                    if (d < 0 || d > 9)
-                    {
-                        ThrowForInvalidData();
-                    }
-                    result = negative ? checked((result * 10) - d) : checked((result * 10) + d);
-
-                    p++;
-                }
-            }
-
-            Debug.Assert(result == long.Parse(ExtractCurrent()), "Expected manually parsed result to match Parse result");
-            return result;
-        }
-
-        /// <summary>Moves to the next component and parses it as a UInt32.</summary>
-        public unsafe uint ParseNextUInt32()
-        {
-            MoveNextOrFail();
-            if (_startIndex == _endIndex)
-            {
-                ThrowForInvalidData();
-            }
-
-            uint result = 0;
-            fixed (char* bufferPtr = _buffer)
-            {
-                char* p = bufferPtr + _startIndex;
-                char* end = bufferPtr + _endIndex;
-                while (p != end)
-                {
-                    int d = *p - '0';
-                    if (d < 0 || d > 9)
-                    {
-                        ThrowForInvalidData();
-                    }
-                    result = (uint)checked((result * 10) + d);
-
-                    p++;
-                }
-            }
-
-            Debug.Assert(result == uint.Parse(ExtractCurrent()), "Expected manually parsed result to match Parse result");
-            return result;
-        }
-
-        /// <summary>Moves to the next component and parses it as a UInt64.</summary>
-        public unsafe ulong ParseNextUInt64()
-        {
-            MoveNextOrFail();
-
-            ulong result = 0;
-            fixed (char* bufferPtr = _buffer)
-            {
-                char* p = bufferPtr + _startIndex;
-                char* end = bufferPtr + _endIndex;
-                while (p != end)
-                {
-                    int d = *p - '0';
-                    if (d < 0 || d > 9)
-                    {
-                        ThrowForInvalidData();
-                    }
-                    result = checked((result * 10ul) + (ulong)d);
-
-                    p++;
-                }
-            }
-
-            Debug.Assert(result == ulong.Parse(ExtractCurrent()), "Expected manually parsed result to match Parse result");
-            return result;
-        }
-
-        /// <summary>Moves to the next component and parses it as a Char.</summary>
-        public char ParseNextChar()
-        {
-            MoveNextOrFail();
-
-            if (_endIndex - _startIndex != 1)
-            {
-                ThrowForInvalidData();
-            }
-            char result = _buffer[_startIndex];
-
-            Debug.Assert(result == char.Parse(ExtractCurrent()), "Expected manually parsed result to match Parse result");
-            return result;
-        }
-
-        internal delegate T ParseRawFunc<T>(string buffer, ref int startIndex, ref int endIndex);
-
-        /// <summary>
-        /// Moves to the next component and hands the raw buffer and indexing data to a selector function
-        /// that can validate and return the appropriate data from the component.
-        /// </summary>
-        internal T ParseRaw<T>(ParseRawFunc<T> selector)
-        {
-            MoveNextOrFail();
-            return selector(_buffer, ref _startIndex, ref _endIndex);
-        }
-
-        /// <summary>
-        /// Gets the current subcomponent and all remaining components of the string as a string.
-        /// </summary>
-        public string ExtractCurrentToEnd()
-        {
-            if (_buffer == null || _startIndex == -1)
-            {
-                throw new InvalidOperationException();
-            }
-            return _buffer.Substring(_startIndex);
-        }
-
-        /// <summary>Throws unconditionally for invalid data.</summary>
-        private static void ThrowForInvalidData()
-        {
-            throw new InvalidDataException();
-        }
-    }
-}
index 3cf98d8476344722eeb8471052a3b947751b5ec0..d1b91a1c0e4d8aae6ff43bd753188c62405f76e8 100644 (file)
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.30" />
+    <PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.38" />
   </ItemGroup>
 
   <ItemGroup>
     <ProjectReference Include="..\..\Microsoft.Diagnostic.Repl\Microsoft.Diagnostic.Repl.csproj" />
+    <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Microsoft.Diagnostics.Tools.RuntimeClient\Microsoft.Diagnostics.Tools.RuntimeClient.csproj" />
   </ItemGroup>
 
 </Project>
\ No newline at end of file