added intermittent mem spike endpoint
authorMario Hewardt <marioh@microsoft.com>
Thu, 9 May 2019 21:16:55 +0000 (14:16 -0700)
committerMario Hewardt <marioh@microsoft.com>
Thu, 9 May 2019 21:16:55 +0000 (14:16 -0700)
documentation/tutorial/src/triggerdump/CounterPayload.cs [new file with mode: 0644]
documentation/tutorial/src/triggerdump/CounterProvider.cs [new file with mode: 0644]
documentation/tutorial/src/triggerdump/KnownData.cs [new file with mode: 0644]
documentation/tutorial/src/triggerdump/Program.cs [new file with mode: 0644]
documentation/tutorial/src/triggerdump/runtimeconfig.template.json [new file with mode: 0644]
documentation/tutorial/src/triggerdump/triggerdump.csproj [new file with mode: 0644]

diff --git a/documentation/tutorial/src/triggerdump/CounterPayload.cs b/documentation/tutorial/src/triggerdump/CounterPayload.cs
new file mode 100644 (file)
index 0000000..8a8e751
--- /dev/null
@@ -0,0 +1,76 @@
+// 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;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Microsoft.Diagnostics.Tools.Counters
+{
+    public interface ICounterPayload
+    {
+        string GetName();
+        string GetValue();
+        string GetDisplay();
+    }
+
+
+    class CounterPayload : ICounterPayload
+    {
+        public string m_Name;
+        public string m_Value;
+        public string m_DisplayName;
+        public CounterPayload(IDictionary<string, object> payloadFields)
+        {
+            m_Name = payloadFields["Name"].ToString();
+            m_Value = payloadFields["Mean"].ToString();
+            m_DisplayName = payloadFields["DisplayName"].ToString();
+        }
+
+        public string GetName()
+        {
+            return m_Name;
+        }
+
+        public string GetValue()
+        {
+            return m_Value;
+        }
+
+        public string GetDisplay()
+        {
+            return m_DisplayName;
+        }
+    }
+
+    class IncrementingCounterPayload : ICounterPayload
+    {
+        public string m_Name;
+        public string m_Value;
+        public string m_DisplayName;
+        public string m_DisplayRateTimeScale;
+        public IncrementingCounterPayload(IDictionary<string, object> payloadFields)
+        {
+            m_Name = payloadFields["Name"].ToString();
+            m_Value = payloadFields["Increment"].ToString();
+            m_DisplayName = payloadFields["DisplayName"].ToString();
+            m_DisplayRateTimeScale = TimeSpan.Parse(payloadFields["DisplayRateTimeScale"].ToString()).ToString("%s' sec'");
+        }
+
+        public string GetName()
+        {
+            return m_Name;
+        }
+
+        public string GetValue()
+        {
+            return m_Value;
+        }
+
+        public string GetDisplay()
+        {
+            return $"{m_DisplayName} / {m_DisplayRateTimeScale}";
+        }
+    }
+}
diff --git a/documentation/tutorial/src/triggerdump/CounterProvider.cs b/documentation/tutorial/src/triggerdump/CounterProvider.cs
new file mode 100644 (file)
index 0000000..0740007
--- /dev/null
@@ -0,0 +1,59 @@
+// 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;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.Diagnostics.Tools.Counters
+{
+    public class CounterProvider
+    {
+        public string Name { get; }
+        public string Description { get; }
+        public string Keywords { get; }
+        public string Level { get; }
+        public Dictionary<string, CounterProfile> Counters { get; }
+
+        public CounterProvider(string name, string description, string keywords, string level, IEnumerable<CounterProfile> counters)
+        {
+            Name = name;
+            Description = description;
+            Keywords = keywords;
+            Level = level;
+            Counters = new Dictionary<string, CounterProfile>();
+            foreach (CounterProfile counter in counters)
+            {
+                Counters.Add(counter.Name, counter);
+            }
+        }
+
+        public string TryGetDisplayName(string counterName)
+        {
+            if (Counters.ContainsKey(counterName))
+                return Counters[counterName].DisplayName;
+            return null;
+        }
+
+        public string ToProviderString(int interval)
+        {
+            return $"{Name}:{Keywords}:{Level}:EventCounterIntervalSec={interval}";
+        }
+
+        public static string SerializeUnknownProviderName(string unknownCounterProviderName, int interval)
+        {
+            return $"{unknownCounterProviderName}:ffffffff:4:EventCounterIntervalSec={interval}";
+        }
+
+        public IReadOnlyList<CounterProfile> GetAllCounters() => Counters.Values.ToList();
+
+    }
+
+    public class CounterProfile
+    {
+        public string Name { get; set; }
+        public string DisplayName { get; set; }
+        public string Description { get; set; }
+    }
+}
diff --git a/documentation/tutorial/src/triggerdump/KnownData.cs b/documentation/tutorial/src/triggerdump/KnownData.cs
new file mode 100644 (file)
index 0000000..e8903b3
--- /dev/null
@@ -0,0 +1,42 @@
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics.Tracing;
+using System.Linq;
+using Microsoft.Diagnostics.Tracing.Parsers;
+
+namespace Microsoft.Diagnostics.Tools.Counters
+{
+    internal static class KnownData
+    {
+        private static readonly IReadOnlyDictionary<string, CounterProvider> _knownProviders =
+            CreateKnownProviders().ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase);
+
+
+        private static IEnumerable<CounterProvider> CreateKnownProviders()
+        {
+            yield return new CounterProvider(
+                "System.Runtime", // Name
+                "A default set of performance counters provided by the .NET runtime.", // Description
+                "0xffffffff", // Keywords
+                "5", // Level 
+                new[] { // Counters
+                    new CounterProfile{ Name="cpu-usage", Description="Amount of time the process has utilized the CPU (ms)", DisplayName="CPU Usage (%)" },
+                    new CounterProfile{ Name="working-set", Description="Amount of working set used by the process (MB)", DisplayName="Working Set (MB)" },
+                    new CounterProfile{ Name="gc-heap-size", Description="Total heap size reported by the GC (MB)", DisplayName="GC Heap Size (MB)" },
+                    new CounterProfile{ Name="gen-0-gc-count", Description="Number of Gen 0 GCs / sec", DisplayName="Gen 0 GC / sec" },
+                    new CounterProfile{ Name="gen-1-gc-count", Description="Number of Gen 1 GCs / sec", DisplayName="Gen 1 GC / sec" },
+                    new CounterProfile{ Name="gen-2-gc-count", Description="Number of Gen 2 GCs / sec", DisplayName="Gen 2 GC / sec" },
+                    new CounterProfile{ Name="exception-count", Description="Number of Exceptions / sec", DisplayName="Exceptions / sec" },
+                });
+            // TODO: Add more providers (ex. ASP.NET ones)
+        }
+
+        public static IReadOnlyList<CounterProvider> GetAllProviders() => _knownProviders.Values.ToList();
+
+        public static bool TryGetProvider(string providerName, out CounterProvider provider) => _knownProviders.TryGetValue(providerName, out provider);
+    }
+}
diff --git a/documentation/tutorial/src/triggerdump/Program.cs b/documentation/tutorial/src/triggerdump/Program.cs
new file mode 100644 (file)
index 0000000..07561ea
--- /dev/null
@@ -0,0 +1,138 @@
+// 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;
+using System.CommandLine;
+using System.CommandLine.Builder;
+using System.CommandLine.Invocation;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+
+using Microsoft.Diagnostics.Tools.RuntimeClient;
+using Microsoft.Diagnostics.Tracing;
+
+namespace Microsoft.Diagnostics.Tools.Counters
+{
+    internal class Program
+    {
+        private static ulong _sessionId;
+        private static int threshold;
+        private static int pid; 
+
+
+        private static void Main(string[] args)
+        {
+            if(args.Length<2)
+            {
+                Console.WriteLine("triggerdump <pid> <mem threshold in MB>");
+            }
+            else
+            {
+                    pid = Convert.ToInt32(args[0]);
+                    threshold = Convert.ToInt32(args[1]);
+                    CounterProvider provider=null;
+                    StringBuilder sb = new StringBuilder("");
+                    KnownData.TryGetProvider("System.Runtime", out provider);
+                    string prov=provider.ToProviderString(1);    
+
+                    Task monitorTask = new Task(() => 
+                    {
+                        var configuration = new SessionConfiguration(
+                        circularBufferSizeMB: 1000,
+                        outputPath: "",
+                        providers: Trace.Extensions.ToProviders(prov));
+
+                        
+                        var binaryReader = EventPipeClient.CollectTracing(Int32.Parse(args[0]), configuration, out _sessionId);
+                        EventPipeEventSource source = new EventPipeEventSource(binaryReader);
+                        source.Dynamic.All += Dynamic_All;
+                        source.Process();
+                    });
+
+                    Task commandTask = new Task(() =>
+                    {
+                        while(true)
+                        {
+                            while (!Console.KeyAvailable) { }
+                            ConsoleKey cmd = Console.ReadKey(true).Key;
+                            if (cmd == ConsoleKey.Q)
+                            {
+                                break;
+                            }
+                        }
+                    });
+
+                    monitorTask.Start();
+                    commandTask.Start();
+                    commandTask.Wait();
+
+                    try
+                    {
+                        EventPipeClient.StopTracing(Int32.Parse(args[0]), _sessionId);    
+                    }
+                    catch (System.IO.EndOfStreamException) {} 
+            }
+        }
+
+        private static void Dynamic_All(TraceEvent obj)
+        {
+            if (obj.EventName.Equals("EventCounters"))
+            {
+                IDictionary<string, object> payloadVal = (IDictionary<string, object>)(obj.PayloadValue(0));
+                IDictionary<string, object> payloadFields = (IDictionary<string, object>)(payloadVal["Payload"]);
+
+                ICounterPayload payload = payloadFields.Count == 6 ? (ICounterPayload)new IncrementingCounterPayload(payloadFields) : (ICounterPayload)new CounterPayload(payloadFields);
+                string displayName = payload.GetDisplay();                
+                if (string.IsNullOrEmpty(displayName))
+                {
+                    displayName = payload.GetName();
+                }
+
+                if(string.Compare(displayName, "GC Heap Size") == 0 && Convert.ToInt32(payload.GetValue())>threshold)
+                {
+                    Console.WriteLine("Memory threshold has been breached....");
+                    System.Diagnostics.Process process = System.Diagnostics.Process.GetProcessById(pid);
+
+                    System.Diagnostics.ProcessModule coreclr = process.Modules.Cast<System.Diagnostics.ProcessModule>().FirstOrDefault(m => string.Equals(m.ModuleName, "libcoreclr.so"));
+                    if (coreclr == null)
+                    {
+                        Console.WriteLine("Unable to locate .NET runtime associated with this process!");
+                        Environment.Exit(1);
+                    }
+                    else
+                    {
+                        string runtimeDirectory = Path.GetDirectoryName(coreclr.FileName);
+                        string createDumpPath = Path.Combine(runtimeDirectory, "createdump");
+                        if (!File.Exists(createDumpPath))
+                        {
+                            Console.WriteLine("Unable to locate 'createdump' tool in '{runtimeDirectory}'");
+                            Environment.Exit(1);                            
+                        }                        
+
+                        var createdump = new System.Diagnostics.Process()
+                        {       
+                            StartInfo = new System.Diagnostics.ProcessStartInfo()
+                            {
+                                FileName = createDumpPath,
+                                Arguments = $"--name coredump --withheap {pid}",
+                            },
+                            EnableRaisingEvents = true,
+                        };
+
+                        createdump.Start();
+                        createdump.WaitForExit();
+
+                        Environment.Exit(0);
+                    }
+                }
+            }
+        }
+
+    }
+}
diff --git a/documentation/tutorial/src/triggerdump/runtimeconfig.template.json b/documentation/tutorial/src/triggerdump/runtimeconfig.template.json
new file mode 100644 (file)
index 0000000..f022b7f
--- /dev/null
@@ -0,0 +1,3 @@
+{
+  "rollForwardOnNoCandidateFx": 2
+}
\ No newline at end of file
diff --git a/documentation/tutorial/src/triggerdump/triggerdump.csproj b/documentation/tutorial/src/triggerdump/triggerdump.csproj
new file mode 100644 (file)
index 0000000..188e171
--- /dev/null
@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp2.1</TargetFramework>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <ToolCommandName>triggerdump</ToolCommandName>
+    <RootNamespace>Diagnostics.TriggerDump</RootNamespace>
+    <Description>Trigger dump</Description>
+    <PackageTags>Diagnostic</PackageTags>
+    <PackageReleaseNotes>$(Description)</PackageReleaseNotes>
+    <TargetName>triggerdump</TargetName>
+       <OutputType>Exe</OutputType>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Include="$(MSBuildThisFileDirectory)..\..\..\..\src\Tools\dotnet-trace\Extensions.cs" Link="Extensions.cs" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\..\..\src\Microsoft.Diagnostic.Repl\Microsoft.Diagnostic.Repl.csproj" />
+    <ProjectReference Include="$(MSBuildThisFileDirectory)..\..\..\..\src\Microsoft.Diagnostics.Tools.RuntimeClient\Microsoft.Diagnostics.Tools.RuntimeClient.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.41" />
+  </ItemGroup>
+
+</Project>