+++ /dev/null
-// 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 Microsoft.Diagnostics.Tracing.Etlx;
-
-namespace Microsoft.Diagnostics.Tools.Analyze
-{
- public class AnalysisSession
- {
- public MemoryDump Dump { get; }
- public TraceLog Trace { get; }
-
- public AnalysisSession(MemoryDump dump, TraceLog trace)
- {
- Dump = dump;
- Trace = trace;
- }
- }
-}
+++ /dev/null
-// 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.Threading.Tasks;
-using McMaster.Extensions.CommandLineUtils;
-using Microsoft.Diagnostics.Runtime;
-
-namespace Microsoft.Diagnostics.Tools.Analyze
-{
- internal class AsyncHangAnalyzer
- {
- private const string AsyncStateMachineBoxTypeName = "System.Runtime.CompilerServices.AsyncTaskMethodBuilder+AsyncStateMachineBox<";
- private const string DebugFinalizableBoxTypeName = "System.Runtime.CompilerServices.AsyncTaskMethodBuilder+DebugFinalizableAsyncStateMachineBox<";
-
- internal const int TASK_STATE_STARTED = 0x10000; //bin: 0000 0000 0000 0001 0000 0000 0000 0000
- internal const int TASK_STATE_DELEGATE_INVOKED = 0x20000; //bin: 0000 0000 0000 0010 0000 0000 0000 0000
- internal const int TASK_STATE_DISPOSED = 0x40000; //bin: 0000 0000 0000 0100 0000 0000 0000 0000
- internal const int TASK_STATE_EXCEPTIONOBSERVEDBYPARENT = 0x80000; //bin: 0000 0000 0000 1000 0000 0000 0000 0000
- internal const int TASK_STATE_CANCELLATIONACKNOWLEDGED = 0x100000; //bin: 0000 0000 0001 0000 0000 0000 0000 0000
- internal const int TASK_STATE_FAULTED = 0x200000; //bin: 0000 0000 0010 0000 0000 0000 0000 0000
- internal const int TASK_STATE_CANCELED = 0x400000; //bin: 0000 0000 0100 0000 0000 0000 0000 0000
- internal const int TASK_STATE_WAITING_ON_CHILDREN = 0x800000; //bin: 0000 0000 1000 0000 0000 0000 0000 0000
- internal const int TASK_STATE_RAN_TO_COMPLETION = 0x1000000; //bin: 0000 0001 0000 0000 0000 0000 0000 0000
- internal const int TASK_STATE_WAITINGFORACTIVATION = 0x2000000; //bin: 0000 0010 0000 0000 0000 0000 0000 0000
- internal const int TASK_STATE_COMPLETION_RESERVED = 0x4000000; //bin: 0000 0100 0000 0000 0000 0000 0000 0000
- internal const int TASK_STATE_THREAD_WAS_ABORTED = 0x8000000; //bin: 0000 1000 0000 0000 0000 0000 0000 0000
- internal const int TASK_STATE_WAIT_COMPLETION_NOTIFICATION = 0x10000000; //bin: 0001 0000 0000 0000 0000 0000 0000 0000
- internal const int TASK_STATE_EXECUTIONCONTEXT_IS_NULL = 0x20000000; //bin: 0010 0000 0000 0000 0000 0000 0000 0000
- internal const int TASK_STATE_TASKSCHEDULED_WAS_FIRED = 0x40000000; //bin: 0100 0000 0000 0000 0000 0000 0000 0000
-
- public static void Run(IConsole console, ClrRuntime runtime)
- {
- // Collect all state machines
- foreach (var obj in runtime.Heap.EnumerateObjects())
- {
- // Skip non-matching types
- if (!obj.Type.Name.StartsWith(AsyncStateMachineBoxTypeName) && !obj.Type.Name.StartsWith(DebugFinalizableBoxTypeName))
- {
- continue;
- }
-
- // Get the status of the task
- var taskState = obj.GetField<int>("m_stateFlags");
- var taskStatus = ToTaskStatus(taskState);
-
- if(taskStatus == TaskStatus.Canceled || taskStatus == TaskStatus.Faulted || taskStatus == TaskStatus.RanToCompletion)
- {
- continue;
- }
-
- // Get the state machine field
- var field = obj.Type.GetFieldByName("StateMachine");
-
- // Get address and method table
- if (field.ElementType == ClrElementType.Struct)
- {
- var stateMachine = obj.GetValueClassField("StateMachine");
-
- // Exclude Microsoft/System state machines
- if (stateMachine.Type.Name.StartsWith("System") || stateMachine.Type.Name.StartsWith("Microsoft"))
- {
- continue;
- }
-
- console.WriteLine($"StateMachine: {stateMachine.Type.Name} struct 0x{stateMachine.Type.MethodTable:X}");
- foreach (var stateMachineField in stateMachine.Type.Fields)
- {
- console.WriteLine($" {stateMachineField.Name}: {stateMachineField.GetDisplayValue(stateMachine)}");
- }
- }
- else
- {
- var stateMachine = obj.GetObjectField("StateMachine");
-
- // Exclude Microsoft/System state machines
- if (stateMachine.Type.Name.StartsWith("System") || stateMachine.Type.Name.StartsWith("Microsoft"))
- {
- continue;
- }
-
- console.WriteLine($"StateMachine: {stateMachine.Type.Name} class 0x{stateMachine.Type.MethodTable:X}");
- foreach (var stateMachineField in stateMachine.Type.Fields)
- {
- console.WriteLine($" {stateMachineField.Name}: {stateMachineField.GetDisplayValue(stateMachine)}");
- }
- }
- }
- }
-
- private static TaskStatus ToTaskStatus(int stateFlags)
- {
- TaskStatus rval;
-
- if ((stateFlags & TASK_STATE_FAULTED) != 0)
- {
- rval = TaskStatus.Faulted;
- }
- else if ((stateFlags & TASK_STATE_CANCELED) != 0)
- {
- rval = TaskStatus.Canceled;
- }
- else if ((stateFlags & TASK_STATE_RAN_TO_COMPLETION) != 0)
- {
- rval = TaskStatus.RanToCompletion;
- }
- else if ((stateFlags & TASK_STATE_WAITING_ON_CHILDREN) != 0)
- {
- rval = TaskStatus.WaitingForChildrenToComplete;
- }
- else if ((stateFlags & TASK_STATE_DELEGATE_INVOKED) != 0)
- {
- rval = TaskStatus.Running;
- }
- else if ((stateFlags & TASK_STATE_STARTED) != 0)
- {
- rval = TaskStatus.WaitingToRun;
- }
- else if ((stateFlags & TASK_STATE_WAITINGFORACTIVATION) != 0)
- {
- rval = TaskStatus.WaitingForActivation;
- }
- else
- {
- rval = TaskStatus.Created;
- }
-
- return rval;
- }
- }
-}
+++ /dev/null
-// 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 Microsoft.Diagnostics.Runtime;
-
-namespace Microsoft.Diagnostics.Tools.Analyze
-{
- internal static class ClrMdExtensions
- {
- public static bool IsDerivedFrom(this ClrType self, string typeName)
- {
- while (self != null)
- {
- if (string.Equals(self.Name, typeName))
- {
- return true;
- }
- self = self.BaseType;
- }
-
- return false;
- }
-
- public static string GetDisplayValue(this ClrInstanceField self, ClrValueClass clrObject)
- {
- if (self.IsObjectReference)
- {
- var obj = clrObject.GetObjectField(self.Name);
- if (obj.IsNull)
- {
- return "null";
- }
- return $"0x{obj.Address:X} [{obj.Type.Name}:0x{obj.Type.MethodTable:X}]";
- }
- else if(self.HasSimpleValue)
- {
- return $"{self.GetValue(clrObject.Address)} [{self.Type.Name}]";
- }
- else if(self.IsValueClass)
- {
- var vt = clrObject.GetValueClassField(self.Name);
- return $"0x{vt.Address:X} [struct {vt.Type.Name}:0x{vt.Type.MethodTable:X}]";
- }
- else
- {
- return "<unknown value>";
- }
- }
-
- public static string GetDisplayValue(this ClrInstanceField self, ClrObject clrObject)
- {
- if (self.IsObjectReference)
- {
- var obj = clrObject.GetObjectField(self.Name);
- if (obj.IsNull)
- {
- return "null";
- }
- return $"0x{obj.Address:X} [{obj.Type.Name}:0x{obj.Type.MethodTable:X}]";
- }
- else if(self.HasSimpleValue)
- {
- return self.GetValue(clrObject.Address).ToString();
- }
- else if(self.IsValueClass)
- {
- var vt = clrObject.GetValueClassField(self.Name);
- return $"0x{vt.Address:X} [struct {vt.Type.Name}:0x{vt.Type.MethodTable:X}]";
- }
- else
- {
- return "<unknown value>";
- }
- }
- }
-}
+++ /dev/null
-// 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;
-using System.Threading;
-using System.Threading.Tasks;
-using McMaster.Extensions.CommandLineUtils;
-using Microsoft.Diagnostics.Tools.Analyze.Commands;
-
-namespace Microsoft.Diagnostics.Tools.Analyze
-{
- public class CommandProcessor
- {
- private readonly IList<IAnalysisCommand> _commands;
- private readonly IDictionary<string, IAnalysisCommand> _commandNames;
-
- public CommandProcessor()
- {
- _commands = GetCommands();
- _commandNames = BuildCommandNamesIndex(_commands);
- }
-
- public async Task RunAsync(IConsole console, AnalysisSession session, CancellationToken cancellationToken = default)
- {
- await console.Out.WriteLineAsync("Ready to process analysis commands. Type 'help' to list available commands or 'help [command]' to get detailed help on a command.");
- await console.Out.WriteLineAsync("Type 'quit' or 'exit' to exit the analysis session.");
- while (!cancellationToken.IsCancellationRequested)
- {
- await console.Out.WriteAsync("> ");
- var line = await console.In.ReadLineAsync();
- cancellationToken.ThrowIfCancellationRequested();
-
- // Naive arg parsing
- var args = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
- if (string.Equals(args[0], "quit", StringComparison.OrdinalIgnoreCase) || string.Equals(args[0], "q", StringComparison.OrdinalIgnoreCase) || string.Equals(args[0], "exit", StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- if (string.Equals(args[0], "help", StringComparison.OrdinalIgnoreCase))
- {
- await ShowHelpAsync(console, args.AsMemory().Slice(1));
- }
- else if (_commandNames.TryGetValue(args[0], out var command))
- {
- await command.RunAsync(console, args.Skip(1).ToArray(), session);
- }
- else
- {
- console.Error.WriteLine($"Unknown command: {args[0]}");
- }
- }
- }
-
- private async Task ShowHelpAsync(IConsole console, ReadOnlyMemory<string> args)
- {
- if (args.Length == 0)
- {
- foreach (var command in _commands)
- {
- var line = $"* {command.Names[0]} - {command.Description}";
- if (command.Names.Count > 1)
- {
- line += $" (aliases: {string.Join(", ", command.Names.Skip(1))})";
- }
- console.WriteLine(line);
- }
- }
- else
- {
- if (_commandNames.TryGetValue(args.Span[0], out var command))
- {
- await command.WriteHelpAsync(console);
- }
- else
- {
- console.WriteLine($"Unknown command: {args.Span[0]}");
- }
- }
- }
-
- private IDictionary<string, IAnalysisCommand> BuildCommandNamesIndex(IEnumerable<IAnalysisCommand> commands)
- {
- var dict = new Dictionary<string, IAnalysisCommand>(StringComparer.OrdinalIgnoreCase);
- foreach (var command in commands)
- {
- foreach (var name in command.Names)
- {
- dict[name] = command;
- }
- }
- return dict;
- }
-
- private static List<IAnalysisCommand> GetCommands()
- {
- return typeof(Program).Assembly
- .GetExportedTypes()
- .Where(t => !t.IsAbstract && typeof(IAnalysisCommand).IsAssignableFrom(t))
- .Select(t => (IAnalysisCommand)Activator.CreateInstance(t))
- .ToList();
- }
- }
-}
+++ /dev/null
-// 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.Collections.Generic;
-using System.Threading.Tasks;
-using McMaster.Extensions.CommandLineUtils;
-
-namespace Microsoft.Diagnostics.Tools.Analyze.Commands
-{
- public abstract class DumpCommandBase : IAnalysisCommand
- {
- public abstract IReadOnlyList<string> Names { get; }
- public abstract string Description { get; }
-
- public async Task RunAsync(IConsole console, string[] args, AnalysisSession session)
- {
- if (session.Dump == null)
- {
- await console.Error.WriteLineAsync("This command requires a memory dump!");
- }
- else
- {
- await RunAsyncCoreAsync(console, args, session, session.Dump);
- }
- }
-
- public abstract Task WriteHelpAsync(IConsole console);
- protected abstract Task RunAsyncCoreAsync(IConsole console, string[] args, AnalysisSession session, MemoryDump dump);
- }
-}
+++ /dev/null
-// 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.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using McMaster.Extensions.CommandLineUtils;
-
-namespace Microsoft.Diagnostics.Tools.Analyze.Commands
-{
- public class DumpHeapCommand : DumpCommandBase
- {
- public override IReadOnlyList<string> Names { get; } = new[] { "DumpHeap" };
-
- public override string Description => "Dumps objects from the .NET Heap";
-
- protected override Task RunAsyncCoreAsync(IConsole console, string[] args, AnalysisSession session, MemoryDump dump)
- {
- var stats = dump.ComputeHeapStatistics().OrderBy(s => s.TotalSize);
- console.WriteLine(" MT Count TotalSize Class Name");
- foreach (var heapStats in stats)
- {
- console.WriteLine($"{heapStats.Type.MethodTable:X16} {heapStats.Count.ToString().PadLeft(8)} {heapStats.TotalSize.ToString().PadLeft(12)} {heapStats.Type.Name}");
- }
-
- return Task.CompletedTask;
- }
-
- public override Task WriteHelpAsync(IConsole console)
- {
- console.WriteLine("TODO");
- return Task.CompletedTask;
- }
- }
-}
+++ /dev/null
-// 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.Threading.Tasks;
-using McMaster.Extensions.CommandLineUtils;
-using Microsoft.Diagnostics.Runtime;
-
-namespace Microsoft.Diagnostics.Tools.Analyze.Commands
-{
- public class DumpStackCommand : DumpCommandBase
- {
- public override IReadOnlyList<string> Names { get; } = new [] { "dumpstack" };
-
- public override string Description => "Dumps the managed stack trace for the current thread.";
-
- protected override async Task RunAsyncCoreAsync(IConsole console, string[] args, AnalysisSession session, MemoryDump dump)
- {
- foreach(var frame in dump.ActiveThread.EnumerateStackTrace())
- {
- var methodInfo = frame.Method == null ? "<Unknown Method>" : GenerateMethodInfo(frame.Method);
- await console.Out.WriteLineAsync($"{frame.StackPointer:x16} {frame.InstructionPointer:x16} {methodInfo}");
- }
- }
-
- private string GenerateMethodInfo(ClrMethod method)
- {
- return $"{method.Type.Name}.{method.Name}";
- }
-
- public override Task WriteHelpAsync(IConsole console)
- {
- console.WriteLine("TODO");
- return Task.CompletedTask;
- }
- }
-}
+++ /dev/null
-// 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.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading.Tasks;
-using McMaster.Extensions.CommandLineUtils;
-using Microsoft.Diagnostics.Tracing.Etlx;
-
-namespace Microsoft.Diagnostics.Tools.Analyze.Commands
-{
- public class EventStackCommand : TraceCommandBase
- {
- public override IReadOnlyList<string> Names => new[] { "eventstack" };
-
- public override string Description => "Dumps the stack trace associated with an event, if there is one.";
-
- protected override Task RunAsyncCore(IConsole console, string[] args, AnalysisSession session, TraceLog trace)
- {
- if (args.Length < 1)
- {
- console.Error.WriteLine("Usage: eventstack <eventIndex>");
- return Task.CompletedTask;
- }
-
- if (!int.TryParse(args[0], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var eventIndex))
- {
- console.Error.WriteLine("Usage: eventstack <eventIndex>");
- return Task.CompletedTask;
- }
-
- var evt = trace.Events.ElementAt(eventIndex);
- var stack = evt.CallStack();
- if (stack != null)
- {
- WriteStack(stack, console);
- }
- else
- {
- console.Error.WriteLine($"Unable to find any call stacks for event {eventIndex:X4}!");
- }
-
- return Task.CompletedTask;
- }
-
- private void WriteStack(TraceCallStack stack, IConsole console)
- {
- while (stack != null)
- {
- console.WriteLine($" at {stack.CodeAddress.ModuleName}!{stack.CodeAddress.FullMethodName} + 0x{stack.CodeAddress.ILOffset:X4}");
- stack = stack.Caller;
- }
- }
-
- public override Task WriteHelpAsync(IConsole console)
- {
- console.WriteLine("TODO");
- return Task.CompletedTask;
- }
- }
-}
+++ /dev/null
-// 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.Collections.Generic;
-using System.Threading.Tasks;
-using McMaster.Extensions.CommandLineUtils;
-
-namespace Microsoft.Diagnostics.Tools.Analyze.Commands
-{
- public interface IAnalysisCommand
- {
- IReadOnlyList<string> Names { get; }
- string Description { get; }
-
- Task RunAsync(IConsole console, string[] args, AnalysisSession session);
- Task WriteHelpAsync(IConsole console);
- }
-}
+++ /dev/null
-// 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.Collections.Generic;
-using System.Threading.Tasks;
-using McMaster.Extensions.CommandLineUtils;
-
-namespace Microsoft.Diagnostics.Tools.Analyze.Commands
-{
- public class InfoCommand : IAnalysisCommand
- {
- public IReadOnlyList<string> Names { get; } = new List<string>() { "info" };
- public string Description => "Displays information about the current analysis session";
-
- public Task RunAsync(IConsole console, string[] args, AnalysisSession session)
- {
- if(session.Dump != null)
- {
- console.WriteLine("Memory Dump:");
- console.WriteLine($" CLR Version: {session.Dump.Runtime.ClrInfo.Version}");
- console.WriteLine($" CLR Flavor: {session.Dump.Runtime.ClrInfo.Flavor}");
- }
- else
- {
- console.WriteLine("No Memory Dump Loaded.");
- }
-
- if(session.Trace != null)
- {
- console.WriteLine("Trace:");
- console.WriteLine($" OS: {session.Trace.OSName} {session.Trace.OSVersion}");
- }
- else
- {
- console.WriteLine("No Trace Loaded.");
- }
- return Task.CompletedTask;
- }
-
- public Task WriteHelpAsync(IConsole console)
- {
- console.WriteLine("TODO");
- return Task.CompletedTask;
- }
- }
-}
+++ /dev/null
-// 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.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using McMaster.Extensions.CommandLineUtils;
-using Microsoft.Diagnostics.Tracing;
-using Microsoft.Diagnostics.Tracing.Etlx;
-using Microsoft.Diagnostics.Tracing.Parsers;
-
-namespace Microsoft.Diagnostics.Tools.Analyze.Commands
-{
- public class LogsCommand : TraceCommandBase
- {
- public override IReadOnlyList<string> Names => new[] { "logs" };
-
- public override string Description => "Dumps Microsoft.Extensions.Logging logs.";
-
- protected override Task RunAsyncCore(IConsole console, string[] args, AnalysisSession session, TraceLog trace)
- {
- var prefix = string.Empty;
- if(args.Length > 0) {
- prefix = args[0];
- }
- console.WriteLine("Scanning log events...");
- var events = trace.Events
- .Where(t =>
- string.Equals(t.ProviderName, "Microsoft-Extensions-Logging") &&
- string.Equals(t.EventName, "MessageJson") &&
- ((string)t.PayloadByName("LoggerName")).StartsWith(prefix))
- .Select(e => e.Clone())
- .ToList();
- console.WriteLine("Logs:");
- foreach (var evt in events)
- {
- var log = LogMessage.Load(evt);
- console.WriteLine($"* ({((int)evt.EventIndex).ToString("X4")}) [{log.Timestamp:O}] [{log.Level}] {log.LoggerName}({log.EventId}): {log.Message}");
- foreach(var (key, value) in log.Arguments)
- {
- console.WriteLine($" {key} = {value}");
- }
- }
-
- return Task.CompletedTask;
- }
-
- public override Task WriteHelpAsync(IConsole console)
- {
- console.WriteLine("TODO");
- return Task.CompletedTask;
- }
- }
-}
+++ /dev/null
-// 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.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using McMaster.Extensions.CommandLineUtils;
-using Microsoft.Diagnostics.Tracing;
-using Microsoft.Diagnostics.Tracing.Etlx;
-using Microsoft.Diagnostics.Tracing.Parsers;
-
-namespace Microsoft.Diagnostics.Tools.Analyze.Commands
-{
- public class RequestsCommand : TraceCommandBase
- {
- public override IReadOnlyList<string> Names => new[] { "requests" };
-
- public override string Description => "Lists all ASP.NET Core HTTP requests contained in the attached trace (if there is one).";
-
- protected override Task RunAsyncCore(IConsole console, string[] args, AnalysisSession session, TraceLog trace)
- {
- console.WriteLine("Scanning request events...");
- var requests = trace.Events
- .Where(t => string.Equals(t.ProviderName, "Microsoft-AspNetCore-Hosting") && string.Equals(t.EventName, "RequestStart/Start"))
- .Select(e => e.Clone())
- .ToList();
- console.WriteLine("HTTP requests:");
- foreach (var request in requests)
- {
- console.WriteLine($"* ({((int)request.EventIndex).ToString("X4")}) [{request.TimeStamp:O}] {request.PayloadString(0)} {request.PayloadString(1)}");
- }
-
- return Task.CompletedTask;
- }
-
- public override Task WriteHelpAsync(IConsole console)
- {
- console.WriteLine("TODO");
- return Task.CompletedTask;
- }
- }
-}
+++ /dev/null
-// 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.Collections.Generic;
-using System.Threading.Tasks;
-using McMaster.Extensions.CommandLineUtils;
-
-namespace Microsoft.Diagnostics.Tools.Analyze.Commands
-{
- public class ThreadsCommand : DumpCommandBase
- {
- public override IReadOnlyList<string> Names { get; } = new List<string>() { "threads", "~"};
-
- public override string Description => "Lists threads in the current dump.";
-
- protected override async Task RunAsyncCoreAsync(IConsole console, string[] args, AnalysisSession session, MemoryDump dump)
- {
- foreach(var thread in dump.Runtime.Threads)
- {
- var isActive = dump.ActiveThreadId == thread.ManagedThreadId ? "." : " ";
- await console.Out.WriteLineAsync($"{isActive}{thread.ManagedThreadId.ToString().PadLeft(2)} Id: {Utils.FormatAddress(thread.OSThreadId)} Teb: {Utils.FormatAddress(thread.Teb)}");
- }
- }
-
- public override Task WriteHelpAsync(IConsole console)
- {
- console.WriteLine("TODO");
- return Task.CompletedTask;
- }
- }
-}
+++ /dev/null
-// 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.Collections.Generic;
-using System.Threading.Tasks;
-using McMaster.Extensions.CommandLineUtils;
-using Microsoft.Diagnostics.Tracing.Etlx;
-
-namespace Microsoft.Diagnostics.Tools.Analyze.Commands
-{
- public abstract class TraceCommandBase : IAnalysisCommand
- {
- public abstract IReadOnlyList<string> Names { get; }
- public abstract string Description { get; }
-
- public async Task RunAsync(IConsole console, string[] args, AnalysisSession session)
- {
- if (session.Trace == null)
- {
- await console.Error.WriteLineAsync("This command requires an event trace!");
- }
- else
- {
- await RunAsyncCore(console, args, session, session.Trace);
- }
- }
-
- public abstract Task WriteHelpAsync(IConsole console);
- protected abstract Task RunAsyncCore(IConsole console, string[] args, AnalysisSession session, TraceLog trace);
- }
-}
+++ /dev/null
-// 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 Microsoft.Diagnostics.Tracing;
-using Microsoft.Extensions.Logging;
-using Newtonsoft.Json.Linq;
-
-namespace Microsoft.Diagnostics.Tools.Analyze
-{
- internal class LogMessage
- {
- public DateTime Timestamp { get; }
- public string LoggerName { get; }
- public LogLevel Level { get; }
- public EventId EventId { get; }
- public IDictionary<string, string> Arguments { get; }
- public string Message { get; }
-
- public LogMessage(DateTime timestamp, string loggerName, EventId eventId, LogLevel level, IDictionary<string, string> arguments, string message)
- {
- Timestamp = timestamp;
- LoggerName = loggerName;
- EventId = eventId;
- Level = level;
- Arguments = arguments;
- Message = message;
- }
-
- internal static LogMessage Load(TraceEvent request)
- {
- var (arguments, message) = LoadArguments((string)request.PayloadByName("ArgumentsJson"));
- var eventIdStr = (string)request.PayloadByName("EventId");
- EventId eventId = default;
- if (int.TryParse(eventIdStr, out var id))
- {
- eventId = new EventId(id);
- }
- else
- {
- eventId = new EventId(0, eventIdStr);
- }
-
- return new LogMessage(
- request.TimeStamp,
- loggerName: (string)request.PayloadByName("LoggerName"),
- eventId: eventId,
- level: (LogLevel)(int)request.PayloadByName("Level"),
- arguments: arguments,
- message: message);
- }
-
- private static (IDictionary<string, string>, string) LoadArguments(string json)
- {
- var jobj = JObject.Parse(json);
- var arguments = new Dictionary<string, string>();
- var format = "";
- foreach (var prop in jobj.Properties())
- {
- if (prop.Name.Equals("{OriginalFormat}"))
- {
- format = prop.Value.ToString();
- }
- else
- {
- arguments.Add(prop.Name, prop.Value.ToString());
- }
- }
-
- var message = FormatMessage(format, arguments);
- return (arguments, message);
- }
-
- private static string FormatMessage(string format, Dictionary<string, string> arguments)
- {
- foreach (var (key, value) in arguments)
- {
- format = format.Replace($"{{{key}}}", value);
- }
- return format;
- }
- }
-}
+++ /dev/null
-// 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;
-using Microsoft.Diagnostics.Runtime;
-
-namespace Microsoft.Diagnostics.Tools.Analyze
-{
- public class MemoryDump : IDisposable
- {
- public DataTarget Target { get; }
- public ClrRuntime Runtime { get; }
- public int ActiveThreadId { get; set; }
- public ClrThread ActiveThread => GetThread(ActiveThreadId);
-
- public MemoryDump(DataTarget target, ClrRuntime runtime)
- {
- Target = target;
- Runtime = runtime;
-
- var firstThread = runtime.Threads.FirstOrDefault();
- if (firstThread != null)
- {
- ActiveThreadId = firstThread.ManagedThreadId;
- }
- }
-
- public ClrThread GetThread(int id)
- {
- return Runtime.Threads.FirstOrDefault(t => t.ManagedThreadId == id);
- }
-
- public IList<TypeHeapStats> ComputeHeapStatistics()
- {
- // Compute heap information
- var stats = new Dictionary<string, TypeHeapStats>();
- foreach (var obj in Runtime.Heap.EnumerateObjects())
- {
- var type = obj.Type.Name;
- if (!stats.TryGetValue(type, out var heapStats))
- {
- heapStats = new TypeHeapStats(obj.Type);
- stats[type] = heapStats;
- }
-
- heapStats.AddObject(obj.Size);
- }
-
- return stats.Values.ToList();
- }
-
- public void Dispose()
- {
- Target.Dispose();
- }
- }
-}
+++ /dev/null
-// 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.IO;
-using System.Threading.Tasks;
-using McMaster.Extensions.CommandLineUtils;
-using Microsoft.Diagnostics.Runtime;
-using Microsoft.Diagnostics.Tracing.Etlx;
-using Microsoft.Internal.Utilities;
-
-namespace Microsoft.Diagnostics.Tools.Analyze
-{
- [Command(Name = "dotnet-analyze", Description = "Inspect a crash dump using interactive commands")]
- internal class Program
- {
- [Argument(0, "<DIAG_FILES>", Description = "The path to the diagnostic files to analyze.")]
- public IList<string> Files { get; set; }
-
- public async Task<int> OnExecuteAsync(IConsole console, CommandLineApplication app)
- {
- var cleanupFiles = new List<string>();
-
- MemoryDump dump = null;
- TraceLog trace = null;
-
- if (Files == null || Files.Count == 0)
- {
- console.Error.WriteLine("No files were provided!");
- return 1;
- }
-
- try
- {
- foreach (var file in Files)
- {
- if (file.EndsWith(".nettrace"))
- {
- console.WriteLine($"Loading trace: {file} ...");
- var etlx = TraceLog.CreateFromEventPipeDataFile(file);
- console.WriteLine($"Convert trace to: {etlx}.");
- cleanupFiles.Add(etlx);
- trace = TraceLog.OpenOrConvert(etlx);
- }
- else
- {
- console.WriteLine($"Loading crash dump: {file} ...");
- var target = DataTarget.LoadCrashDump(file);
- // Assume there's only one
- if (target.ClrVersions.Count > 1)
- {
- console.Error.WriteLine("Multiple CLR versions are present!");
- return 1;
- }
-
- var runtime = target.ClrVersions[0].CreateRuntime();
- dump = new MemoryDump(target, runtime);
- }
- }
-
- if (dump == null && trace == null)
- {
- console.Error.WriteLine("A dump or trace could not be loaded from the provided files");
- return 1;
- }
- var session = new AnalysisSession(dump, trace);
- await (new CommandProcessor()).RunAsync(console, session, console.GetCtrlCToken());
- return 0;
- }
- finally
- {
- dump?.Dispose();
- trace?.Dispose();
-
- foreach (var file in cleanupFiles)
- {
- if (File.Exists(file))
- {
- File.Delete(file);
- }
- }
- }
- }
-
- private static int Main(string[] args)
- {
- DebugUtil.WaitForDebuggerIfRequested(ref args);
-
- try
- {
- return CommandLineApplication.Execute<Program>(args);
- }
- catch (OperationCanceledException)
- {
- return 0;
- }
- }
- }
-}
+++ /dev/null
-using Microsoft.Diagnostics.Runtime;
-
-namespace Microsoft.Diagnostics.Tools.Analyze
-{
- public class TypeHeapStats
- {
- public ClrType Type { get; }
- public long Count { get; private set; }
- public ulong TotalSize { get; private set; }
-
- public TypeHeapStats(ClrType type)
- {
- Type = type;
- }
-
- public void AddObject(ulong size)
- {
- Count++;
- TotalSize += size;
- }
- }
-}
+++ /dev/null
-namespace Microsoft.Diagnostics.Tools.Analyze
-{
- internal static class Utils
- {
- internal static string FormatAddress(uint addr)
- {
- var hi = (short)(addr >> 16);
- var lo = (short)addr;
- return $"{hi:x4}`{lo:x4}";
- }
-
- internal static string FormatAddress(ulong addr)
- {
- var hi = (int)(addr >> 32);
- var lo = (int)addr;
- return $"{hi:x8}`{lo:x8}";
- }
- }
-}
+++ /dev/null
-<Project Sdk="Microsoft.NET.Sdk">
- <PropertyGroup>
- <OutputType>Exe</OutputType>
- <TargetFramework>netcoreapp2.1</TargetFramework>
- <RuntimeFrameworkVersion>2.1.0</RuntimeFrameworkVersion>
- <!-- Don't pack until ship engineering is done. Currently causing the official job to fail.
- <IsPackable>true</IsPackable>
- <PackAsTool>true</PackAsTool>
- -->
- <PackAsToolShimRuntimeIdentifiers>win-x64;win-x86;osx-x64</PackAsToolShimRuntimeIdentifiers>
- <!-- The package version needs to be hard coded as a stable version so "dotnet tool install -g dotnet-analyze" works -->
- <Version>1.0.0</Version>
- <PackageVersion>1.0.0</PackageVersion>
- <ToolCommandName>dotnet-analyze</ToolCommandName>
- <RootNamespace>Microsoft.Diagnostics.Tools.Analyze</RootNamespace>
- <Description>Diagnostic analyze tool</Description>
- <PackageTags>Diagnostic</PackageTags>
- <PackageReleaseNotes>$(Description)</PackageReleaseNotes>
- <!-- Need to put the shims here to sign -->
- <PackagedShimOutputRootDirectory>$(OutputPath)</PackagedShimOutputRootDirectory>
- </PropertyGroup>
-
- <ItemGroup>
- <Compile Include="..\Common\ConsoleCancellation.cs" Link="ConsoleCancellation.cs" />
- <Compile Include="..\Common\DebugUtil.cs" Link="DebugUtil.cs" />
- </ItemGroup>
-
- <ItemGroup>
- <PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.2.5" />
- <PackageReference Include="Microsoft.Diagnostics.Runtime" Version="$(MicrosoftDiagnosticsRuntimeVersion)" />
- <PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.30" />
- <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.2.0-preview3-35497" />
- <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
- </ItemGroup>
-
-</Project>
+++ /dev/null
-{
- "rollForwardOnNoCandidateFx": 2
-}
\ No newline at end of file