-
Notifications
You must be signed in to change notification settings - Fork 140
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Hashing exceptions with normalization + improved diagnostic capabilities
- Loading branch information
1 parent
6149a19
commit a433b60
Showing
40 changed files
with
783 additions
and
57 deletions.
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
107 changes: 107 additions & 0 deletions
107
tracer/src/Datadog.Trace/Debugger/ExceptionAutoInstrumentation/ExceptionNormalizer.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,107 @@ | ||
// <copyright file="ExceptionNormalizer.cs" company="Datadog"> | ||
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. | ||
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. | ||
// </copyright> | ||
|
||
using System; | ||
using System.Numerics; | ||
using System.Runtime.CompilerServices; | ||
using System.Text; | ||
using Datadog.Trace.VendoredMicrosoftCode.System; | ||
using Fnv1aHash = Datadog.Trace.VendoredMicrosoftCode.System.Reflection.Internal.Hash; | ||
using MemoryExtensions = Datadog.Trace.Debugger.Helpers.MemoryExtensions; | ||
|
||
#nullable enable | ||
namespace Datadog.Trace.Debugger.ExceptionAutoInstrumentation | ||
{ | ||
internal class ExceptionNormalizer | ||
{ | ||
/// <summary> | ||
/// Given the string representation of an exception alongside it's FQN of the outer and (potential) inner exception, | ||
/// this function cleanse the stack trace from error messages, customized information attached to the exception and PDB line info if present. | ||
/// It returns a hash representing the resulting cleansed exception and inner exceptions. | ||
/// Used to aggregate same/similar exceptions that only differ by non-relevant bits. | ||
/// </summary> | ||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
internal int NormalizeAndHashException(string exceptionString, string outerExceptionType, string? innerExceptionType) | ||
{ | ||
if (string.IsNullOrEmpty(exceptionString)) | ||
{ | ||
throw new ArgumentException(@"Exception string cannot be null or empty", nameof(exceptionString)); | ||
} | ||
|
||
var fnvHashCode = Fnv1aHash.FnvOffsetBias; | ||
|
||
fnvHashCode = HashLine(VendoredMicrosoftCode.System.MemoryExtensions.AsSpan(outerExceptionType), fnvHashCode); | ||
|
||
if (innerExceptionType != null) | ||
{ | ||
fnvHashCode = HashLine(VendoredMicrosoftCode.System.MemoryExtensions.AsSpan(innerExceptionType), fnvHashCode); | ||
} | ||
|
||
var exceptionSpan = VendoredMicrosoftCode.System.MemoryExtensions.AsSpan(exceptionString); | ||
|
||
var inSpan = VendoredMicrosoftCode.System.MemoryExtensions.AsSpan(" in "); | ||
|
||
while (!exceptionSpan.IsEmpty) | ||
{ | ||
var lineEndIndex = exceptionSpan.IndexOfAny('\r', '\n'); | ||
VendoredMicrosoftCode.System.ReadOnlySpan<char> line; | ||
|
||
if (lineEndIndex >= 0) | ||
{ | ||
line = exceptionSpan.Slice(0, lineEndIndex); | ||
exceptionSpan = exceptionSpan.Slice(lineEndIndex + 1); | ||
if (!exceptionSpan.IsEmpty && exceptionSpan[0] == '\n') | ||
{ | ||
exceptionSpan = exceptionSpan.Slice(1); | ||
} | ||
} | ||
else | ||
{ | ||
line = exceptionSpan; | ||
exceptionSpan = default; | ||
} | ||
|
||
if (IsStackTraceLine(line) && !IsLambdaFunctionLine(line)) | ||
{ | ||
var index = VendoredMicrosoftCode.System.MemoryExtensions.IndexOf(line, inSpan); | ||
fnvHashCode = HashLine(index > 0 ? line.Slice(0, index) : line, fnvHashCode); | ||
} | ||
} | ||
|
||
return fnvHashCode; | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static bool IsStackTraceLine(VendoredMicrosoftCode.System.ReadOnlySpan<char> line) | ||
{ | ||
int i = 0; | ||
|
||
while (i < line.Length && char.IsWhiteSpace(line[i])) | ||
{ | ||
i++; | ||
} | ||
|
||
return line.Length - i >= 3 && line[i] == 'a' && line[i + 1] == 't' && line[i + 2] == ' '; | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static bool IsLambdaFunctionLine(VendoredMicrosoftCode.System.ReadOnlySpan<char> line) | ||
{ | ||
var lambdaSpan = VendoredMicrosoftCode.System.MemoryExtensions.AsSpan("lambda_"); | ||
return VendoredMicrosoftCode.System.MemoryExtensions.Contains(line, lambdaSpan, StringComparison.Ordinal); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
protected virtual int HashLine(VendoredMicrosoftCode.System.ReadOnlySpan<char> line, int fnvHashCode) | ||
{ | ||
for (var i = 0; i < line.Length; i++) | ||
{ | ||
fnvHashCode = Fnv1aHash.Combine((uint)line[i], fnvHashCode); | ||
} | ||
|
||
return fnvHashCode; | ||
} | ||
} | ||
} |
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
Oops, something went wrong.