-
Notifications
You must be signed in to change notification settings - Fork 90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Portable PDB support to Raygun4Net and Raygun4NetCore #528
Merged
Merged
Changes from 8 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
81a6ab1
Support capturing ILOffset and MetadataToken for the method
xenolightning be13820
Wrap the retrieval of reflected properties in a try catch
xenolightning f1c8e66
Make the -1 value a constant
xenolightning b437013
Add comment on why this is in a try catch
xenolightning 1f1ae72
Add a local file system PE reader to extract the PDB information out …
xenolightning 6782aca
Add a concurrent dictionary to cache the lookups for modules.
xenolightning 3c3ea43
Whoops, actually call the thing.
xenolightning 16a37ae
Pull debug info forward because it catches its own exceptions
xenolightning 2098a7c
Port the code across to framework
xenolightning 589ac25
Conditionally include the package for net standard
xenolightning c6af291
Rename a few of the properties and add an images section for the imag…
xenolightning 6827ed5
Empty check the cache for an early return and null check signature
xenolightning 876544f
Fix typo
xenolightning fe2a4ff
Fix empty lines
xenolightning e471e0d
Update the caching based off Phill's feedback
xenolightning 159a5ee
Add a tests and filter out nulls
xenolightning File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,20 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Concurrent; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Reflection; | ||
using System.Reflection.PortableExecutable; | ||
using System.Text; | ||
using Mindscape.Raygun4Net.Diagnostics; | ||
|
||
namespace Mindscape.Raygun4Net | ||
{ | ||
public class RaygunErrorMessageBuilder | ||
{ | ||
private static readonly ConcurrentDictionary<string, PdbDebugInformation> DebugInformationCache = new(); | ||
public static Func<string, PEReader> AssemblyReaderProvider { get; set; } = PortableExecutableReaderExtensions.GetFileSystemPEReader; | ||
|
||
protected static string FormatTypeName(Type type, bool fullName) | ||
{ | ||
string name = fullName ? type.FullName : type.Name; | ||
|
@@ -24,6 +30,7 @@ protected static string FormatTypeName(Type type, bool fullName) | |
{ | ||
stringBuilder.Append(FormatTypeName(t, false)).Append(","); | ||
} | ||
|
||
stringBuilder.Remove(stringBuilder.Length - 1, 1); | ||
stringBuilder.Append(">"); | ||
|
||
|
@@ -51,31 +58,50 @@ public static RaygunErrorStackTraceLineMessage[] BuildStackTrace(StackTrace stac | |
return lines.ToArray(); | ||
} | ||
|
||
foreach (StackFrame frame in frames) | ||
foreach (var frame in frames) | ||
{ | ||
MethodBase method = frame.GetMethod(); | ||
var method = frame.GetMethod(); | ||
|
||
if (method != null) | ||
{ | ||
int lineNumber = frame.GetFileLineNumber(); | ||
|
||
if (lineNumber == 0) | ||
string methodName = null; | ||
string file = null; | ||
string className = null; | ||
var lineNumber = 0; | ||
var ilOffset = StackFrame.OFFSET_UNKNOWN; | ||
var methodToken = StackFrame.OFFSET_UNKNOWN; | ||
PdbDebugInformation debugInfo = null; | ||
|
||
try | ||
{ | ||
lineNumber = frame.GetILOffset(); | ||
file = frame.GetFileName(); | ||
lineNumber = frame.GetFileLineNumber(); | ||
methodName = GenerateMethodName(method); | ||
className = method.ReflectedType != null ? method.ReflectedType.FullName : "(unknown)"; | ||
ilOffset = frame.GetILOffset(); | ||
debugInfo = TryGetDebugInformation(method.Module.Name); | ||
|
||
// This might fail in medium trust environments or for array methods, | ||
// so don't crash the entire send process - just move on with what we have | ||
methodToken = method.MetadataToken; | ||
} | ||
catch (Exception ex) | ||
{ | ||
Debug.WriteLine("Exception retrieving stack frame details: {0}", ex); | ||
} | ||
|
||
var methodName = GenerateMethodName(method); | ||
|
||
string file = frame.GetFileName(); | ||
|
||
string className = method.DeclaringType != null ? method.DeclaringType.FullName : "(unknown)"; | ||
|
||
var line = new RaygunErrorStackTraceLineMessage | ||
{ | ||
FileName = file, | ||
LineNumber = lineNumber, | ||
MethodName = methodName, | ||
ClassName = className | ||
ClassName = className, | ||
ILOffset = ilOffset, | ||
MethodToken = methodToken, | ||
PdbChecksum = debugInfo?.Checksum, | ||
PdbSignature = debugInfo?.Signature, | ||
PdbFile = debugInfo?.File, | ||
PdbTimestamp = debugInfo?.Timestamp | ||
}; | ||
|
||
lines.Add(line); | ||
|
@@ -173,9 +199,7 @@ public static RaygunErrorMessage Build(Exception exception) | |
message.Data = data; | ||
} | ||
|
||
AggregateException ae = exception as AggregateException; | ||
|
||
if (ae != null && ae.InnerExceptions != null) | ||
if (exception is AggregateException ae) | ||
{ | ||
message.InnerErrors = new RaygunErrorMessage[ae.InnerExceptions.Count]; | ||
int index = 0; | ||
|
@@ -193,5 +217,31 @@ public static RaygunErrorMessage Build(Exception exception) | |
|
||
return message; | ||
} | ||
|
||
private static PdbDebugInformation TryGetDebugInformation(string moduleName) | ||
{ | ||
if (DebugInformationCache.TryGetValue(moduleName, out var cachedInfo)) | ||
{ | ||
return cachedInfo; | ||
} | ||
|
||
try | ||
{ | ||
// Attempt to read out the Debug Info from the PE | ||
var peReader = AssemblyReaderProvider(moduleName); | ||
|
||
if (peReader.TryGetDebugInformation(out var debugInfo)) | ||
{ | ||
DebugInformationCache.TryAdd(moduleName, debugInfo); | ||
return debugInfo; | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
Debug.WriteLine($"Could not load debug information: {ex}"); | ||
} | ||
|
||
return null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment as the other implementation. |
||
} | ||
} | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
Mindscape.Raygun4Net.NetCore.Common/Diagnostics/PdbDebugInformation.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using System; | ||
|
||
namespace Mindscape.Raygun4Net.Diagnostics; | ||
|
||
internal sealed class PdbDebugInformation | ||
{ | ||
/// <summary> | ||
/// The signature of the PE and PDB linking them together - usually a GUID | ||
/// </summary> | ||
public string Signature { get; set; } | ||
|
||
/// <summary> | ||
/// Checksum of the PE & PDB. Format: {algorithm}:{hash:X} | ||
/// </summary> | ||
public string Checksum { get; set; } | ||
|
||
/// <summary> | ||
/// The full location of the PDB at build time | ||
/// </summary> | ||
public string File { get; set; } | ||
|
||
/// <summary> | ||
/// The generated Timestamp of the code at build time stored as hex | ||
/// </summary> | ||
public string Timestamp { get; set; } | ||
|
||
} |
73 changes: 73 additions & 0 deletions
73
Mindscape.Raygun4Net.NetCore.Common/Diagnostics/PortableExecutableReaderExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
#nullable enable | ||
|
||
using System; | ||
using System.Collections.Immutable; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Reflection.PortableExecutable; | ||
|
||
namespace Mindscape.Raygun4Net.Diagnostics; | ||
|
||
internal static class PortableExecutableReaderExtensions | ||
{ | ||
public static PEReader? GetFileSystemPEReader(string moduleName) | ||
{ | ||
try | ||
{ | ||
// Read into memory to avoid any premature stream closures | ||
var bytes = ImmutableArray.Create(File.ReadAllBytes(moduleName)); | ||
return new PEReader(bytes); | ||
} | ||
catch (Exception ex) | ||
{ | ||
Debug.WriteLine($"Could not open module [{moduleName}] from disk: {ex}"); | ||
return null; | ||
} | ||
} | ||
|
||
public static bool TryGetDebugInformation(this PEReader peReader, out PdbDebugInformation? debugInformation) | ||
{ | ||
try | ||
{ | ||
debugInformation = GetDebugInformation(peReader); | ||
return true; | ||
} | ||
catch (Exception ex) | ||
{ | ||
Debug.WriteLine($"Error reading PE Debug Data: {ex}"); | ||
} | ||
|
||
debugInformation = null; | ||
return false; | ||
} | ||
|
||
private static PdbDebugInformation GetDebugInformation(this PEReader peReader) | ||
{ | ||
var debugInfo = new PdbDebugInformation | ||
{ | ||
Timestamp = $"{peReader.PEHeaders.CoffHeader.TimeDateStamp:X8}" | ||
}; | ||
|
||
foreach (var entry in peReader.ReadDebugDirectory()) | ||
{ | ||
if (entry.Type == DebugDirectoryEntryType.CodeView) | ||
{ | ||
// Read the CodeView data | ||
var codeViewData = peReader.ReadCodeViewDebugDirectoryData(entry); | ||
|
||
debugInfo.File = codeViewData.Path; | ||
debugInfo.Signature = codeViewData.Guid.ToString(); | ||
} | ||
|
||
if (entry.Type == DebugDirectoryEntryType.PdbChecksum) | ||
{ | ||
var checksumEntry = peReader.ReadPdbChecksumDebugDirectoryData(entry); | ||
var checksumHex = BitConverter.ToString(checksumEntry.Checksum.ToArray()).Replace("-", "").ToUpperInvariant(); | ||
debugInfo.Checksum = $"{checksumEntry.AlgorithmName}:{checksumHex}"; | ||
} | ||
} | ||
|
||
return debugInfo; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intention here is that this is replaced with an android version for MAUI.
This needs to be bundled inside the raygun4maui release though, as our core library doesn't have platform targets