Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions diagnostics.sln
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Diagnostics.Debug
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "inc", "inc", "{BE45F03E-D700-404F-A890-8ED7D8969958}"
ProjectSection(SolutionItems) = preProject
src\SOS\inc\clrma.h = src\SOS\inc\clrma.h
src\SOS\inc\clrma.idl = src\SOS\inc\clrma.idl
src\SOS\inc\debuggerservices.h = src\SOS\inc\debuggerservices.h
src\SOS\inc\host.h = src\SOS\inc\host.h
src\SOS\inc\hostservices.h = src\SOS\inc\hostservices.h
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
Expand All @@ -22,7 +24,7 @@ public class CrashInfoService : ICrashInfoService
/// </summary>
public const uint FAST_FAIL_EXCEPTION_DOTNET_AOT = 0x48;

public sealed class CrashInfoJson
private sealed class CrashInfoJson
{
[JsonPropertyName("version")]
public string Version { get; set; }
Expand All @@ -48,10 +50,10 @@ public sealed class CrashInfoJson
public string Message { get; set; }

[JsonPropertyName("exception")]
public CrashInfoException Exception { get; set; }
public ExceptionJson Exception { get; set; }
}

public sealed class CrashInfoException : IManagedException
private sealed class ExceptionJson
{
[JsonPropertyName("address")]
[JsonConverter(typeof(HexUInt64Converter))]
Expand All @@ -68,17 +70,13 @@ public sealed class CrashInfoException : IManagedException
public string Type { get; set; }

[JsonPropertyName("stack")]
public CrashInfoStackFrame[] Stack { get; set; }

IEnumerable<IStackFrame> IManagedException.Stack => Stack;
public StackFrameJson[] Stack { get; set; }

[JsonPropertyName("inner")]
public CrashInfoException[] InnerExceptions { get; set; }

IEnumerable<IManagedException> IManagedException.InnerExceptions => InnerExceptions;
public ExceptionJson[] InnerExceptions { get; set; }
}

public sealed class CrashInfoStackFrame : IStackFrame
private sealed class StackFrameJson
{
[JsonPropertyName("ip")]
[JsonConverter(typeof(HexUInt64Converter))]
Expand All @@ -100,7 +98,7 @@ public sealed class CrashInfoStackFrame : IStackFrame
public string MethodName { get; set; }
}

public sealed class HexUInt64Converter : JsonConverter<ulong>
private sealed class HexUInt64Converter : JsonConverter<ulong>
{
public override ulong Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Expand All @@ -117,7 +115,7 @@ public override ulong Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSe
public override void Write(Utf8JsonWriter writer, ulong value, JsonSerializerOptions options) => throw new NotImplementedException();
}

public sealed class HexUInt32Converter : JsonConverter<uint>
private sealed class HexUInt32Converter : JsonConverter<uint>
{
public override uint Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Expand All @@ -134,7 +132,90 @@ public override uint Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSer
public override void Write(Utf8JsonWriter writer, uint value, JsonSerializerOptions options) => throw new NotImplementedException();
}

public static ICrashInfoService Create(uint hresult, ReadOnlySpan<byte> triageBuffer)
private sealed class CrashInfoException : IException
{
private readonly ExceptionJson _exception;
private readonly IModuleService _moduleService;
private IStack _stack;
private IEnumerable<IException> _inner;

public CrashInfoException(ExceptionJson exception, IModuleService moduleService)
{
Debug.Assert(exception != null);
Debug.Assert(moduleService != null);
_exception = exception;
_moduleService = moduleService;
}

public ulong Address => _exception.Address;

public uint HResult => _exception.HResult;

public string Message => _exception.Message;

public string Type => _exception.Type;

public IStack Stack => _stack ??= new CrashInfoStack(_exception.Stack, _moduleService);

public IEnumerable<IException> InnerExceptions => _inner ??= _exception.InnerExceptions != null ? _exception.InnerExceptions.Select((inner) => new CrashInfoException(inner, _moduleService)) : Array.Empty<IException>();
}

private sealed class CrashInfoStack : IStack
{
private readonly IStackFrame[] _stackFrames;

public CrashInfoStack(StackFrameJson[] stackFrames, IModuleService moduleService)
{
_stackFrames = stackFrames != null ? stackFrames.Select((frame) => new CrashInfoStackFrame(frame, moduleService)).ToArray() : Array.Empty<IStackFrame>();
}

public int FrameCount => _stackFrames.Length;

public IStackFrame GetStackFrame(int index) => _stackFrames[index];
}

private sealed class CrashInfoStackFrame : IStackFrame
{
private readonly StackFrameJson _stackFrame;
private readonly IModuleService _moduleService;

public CrashInfoStackFrame(StackFrameJson stackFrame, IModuleService moduleService)
{
Debug.Assert(stackFrame != null);
Debug.Assert(moduleService != null);
_stackFrame = stackFrame;
_moduleService = moduleService;
}

public ulong InstructionPointer => _stackFrame.InstructionPointer;

public ulong StackPointer => _stackFrame.StackPointer;

public ulong ModuleBase => _stackFrame.ModuleBase;

public void GetMethodName(out string moduleName, out string methodName, out ulong displacement)
{
moduleName = null;
methodName = _stackFrame.MethodName;
displacement = _stackFrame.Offset;

if (ModuleBase != 0)
{
IModule module = _moduleService.GetModuleFromBaseAddress(_stackFrame.ModuleBase);
if (module != null)
{
moduleName = Path.GetFileNameWithoutExtension(module.FileName);
if (string.IsNullOrEmpty(methodName))
{
IModuleSymbols moduleSymbols = module.Services.GetService<IModuleSymbols>();
moduleSymbols?.TryGetSymbolName(_stackFrame.InstructionPointer, out methodName, out displacement);
}
}
}
}
}

public static ICrashInfoService Create(uint hresult, ReadOnlySpan<byte> triageBuffer, IModuleService moduleService)
{
CrashInfoService crashInfoService = null;
try
Expand All @@ -145,7 +226,7 @@ public static ICrashInfoService Create(uint hresult, ReadOnlySpan<byte> triageBu
{
if (Version.TryParse(crashInfo.Version, out Version protocolVersion) && protocolVersion.Major >= 1)
{
crashInfoService = new(crashInfo.Thread, hresult, crashInfo);
crashInfoService = new(crashInfo.Thread, hresult, crashInfo, moduleService);
}
else
{
Expand All @@ -157,14 +238,16 @@ public static ICrashInfoService Create(uint hresult, ReadOnlySpan<byte> triageBu
Trace.TraceError($"CrashInfoService: JsonSerializer.Deserialize failed");
}
}
catch (Exception ex) when (ex is JsonException or NotSupportedException or DecoderFallbackException or ArgumentException)
catch (Exception ex) when (ex is System.Text.Json.JsonException or NotSupportedException or DecoderFallbackException or ArgumentException)
{
Trace.TraceError($"CrashInfoService: {ex}");
}
return crashInfoService;
}

private CrashInfoService(uint threadId, uint hresult, CrashInfoJson crashInfo)
private readonly IException _exception;

private CrashInfoService(uint threadId, uint hresult, CrashInfoJson crashInfo, IModuleService moduleService)
{
ThreadId = threadId;
HResult = hresult;
Expand All @@ -173,7 +256,7 @@ private CrashInfoService(uint threadId, uint hresult, CrashInfoJson crashInfo)
RuntimeVersion = crashInfo.RuntimeVersion;
RuntimeType = (RuntimeType)crashInfo.RuntimeType;
Message = crashInfo.Message;
Exception = crashInfo.Exception;
_exception = crashInfo.Exception != null ? new CrashInfoException(crashInfo.Exception, moduleService) : null;
}

#region ICrashInfoService
Expand All @@ -192,7 +275,60 @@ private CrashInfoService(uint threadId, uint hresult, CrashInfoJson crashInfo)

public string Message { get; }

public IManagedException Exception { get; }
public IException GetException(ulong address)
{
// Only supports getting the "current" exception or the crash exception.
if (address != 0)
{
if (address != _exception.Address)
{
throw new ArgumentOutOfRangeException(nameof(address));
}
}
return _exception;
}

public IException GetThreadException(uint threadId)
{
// Only supports getting the "current" exception or the crash thread's exception
if (threadId != 0)
{
if (threadId != ThreadId)
{
throw new ArgumentOutOfRangeException(nameof(threadId));
}
}
return _exception;
}

public IEnumerable<IException> GetNestedExceptions(uint threadId)
{
// Only supports getting the "current" exception or the crash thread's exception
if (threadId != 0)
{
if (threadId != ThreadId)
{
throw new ArgumentOutOfRangeException(nameof(threadId));
}
}

List<IException> exceptions = new() {
_exception
};

AddExceptions(_exception.InnerExceptions);

void AddExceptions(IEnumerable<IException> inner)
{
foreach (IException exception in inner)
{
exceptions.Add(exception);
AddExceptions(exception.InnerExceptions);
}
}

return exceptions;
}

#endregion
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public override Version GetVersionData()
{
if (InitializeValue(Module.Flags.InitializeVersion))
{
if (!_moduleInfo.Version.Equals(Utilities.EmptyVersion))
if (_moduleInfo.Version != null && !_moduleInfo.Version.Equals(Utilities.EmptyVersion))
{
_version = _moduleInfo.Version;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public static ICrashInfoService CreateCrashInfoService(IServiceProvider services
Span<byte> buffer = new byte[triageBufferSize];
if (services.GetService<IMemoryService>().ReadMemory(triageBufferAddress, buffer, out int bytesRead) && bytesRead == triageBufferSize)
{
return CrashInfoService.Create(hresult, buffer);
return CrashInfoService.Create(hresult, buffer, services.GetService<IModuleService>());
}
else
{
Expand Down
24 changes: 22 additions & 2 deletions src/Microsoft.Diagnostics.DebugServices/ICrashInfoService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace Microsoft.Diagnostics.DebugServices
{
Expand Down Expand Up @@ -57,8 +58,27 @@ public interface ICrashInfoService
string Message { get; }

/// <summary>
/// The exception that caused the crash or null
/// The exception at the address
/// </summary>
IManagedException Exception { get; }
/// <param name="address">address of exception object or 0 for current exception</param>
/// <returns>exception or null if none</returns>
/// <exception cref="ArgumentOutOfRangeException">invalid exception address</exception>
IException GetException(ulong address);

/// <summary>
/// Returns the thread's current exception.
/// </summary>
/// <param name="threadId">OS thread id</param>
/// <returns>exception or null if none</returns>
/// <exception cref="ArgumentOutOfRangeException">invalid thread id</exception>
IException GetThreadException(uint threadId);

/// <summary>
/// Returns all the thread's nested exception.
/// </summary>
/// <param name="threadId">OS thread id</param>
/// <returns>array of nested exceptions</returns>
/// <exception cref="ArgumentOutOfRangeException">invalid thread id</exception>
IEnumerable<IException> GetNestedExceptions(uint threadId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Microsoft.Diagnostics.DebugServices
/// <summary>
/// Describes a managed exception
/// </summary>
public interface IManagedException
public interface IException
{
/// <summary>
/// Exception object address
Expand All @@ -31,13 +31,13 @@ public interface IManagedException
uint HResult { get; }

/// <summary>
/// Stack trace of exception
/// Stack trace of exception or null
/// </summary>
IEnumerable<IStackFrame> Stack { get; }
IStack Stack { get; }

/// <summary>
/// The inner exception or exceptions in the AggregateException case
/// </summary>
IEnumerable<IManagedException> InnerExceptions { get; }
IEnumerable<IException> InnerExceptions { get; }
}
}
27 changes: 27 additions & 0 deletions src/Microsoft.Diagnostics.DebugServices/IStack.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace Microsoft.Diagnostics.DebugServices
{
/// <summary>
/// Native or managed stack
/// </summary>
public interface IStack
{
/// <summary>
/// Number of total stack frames
/// </summary>
int FrameCount { get; }

/// <summary>
/// Get an individual stack frame
/// </summary>
/// <param name="index">stack frame index</param>
/// <returns>frame</returns>
/// <exception cref="ArgumentOutOfRangeException">invalid index</exception>
IStackFrame GetStackFrame(int index);
}
}
Loading