Skip to content

Commit

Permalink
Revert "Stack frames improvement - deleted stack frames generated by …
Browse files Browse the repository at this point in the history
…async state machine. Calling assembly fix"

This reverts commit 52b2f0c.
  • Loading branch information
konraddysput committed Apr 9, 2018
1 parent e7414f8 commit aa49c5d
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 113 deletions.
95 changes: 37 additions & 58 deletions Backtrace/Base/BacktraceReportBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
#if !NET35
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
#endif
using System.Text;

namespace Backtrace.Base
Expand Down Expand Up @@ -84,7 +79,7 @@ public string Classifier
/// <summary>
/// Current report exception stack
/// </summary>
public List<DiagnosticStack> DiagnosticStack { get; set; } = new List<DiagnosticStack>();
public List<ExceptionStack> ExceptionStack { get; set; } = new List<ExceptionStack>();

/// <summary>
/// Create new instance of Backtrace report to sending a report with custom client message
Expand Down Expand Up @@ -153,87 +148,71 @@ internal static Dictionary<string, T> ConcatAttributes(
return reportAttributes.Merge(attributes);
}

/// <summary>
/// Set Calling Assembly and current thread stack trace property.
/// CallingAssembly and StackTrace are necessary to prepare diagnostic JSON in BacktraceData class
/// </summary>
private void SetCallingAppInformation()
{
// generate stacktrace with file info
// if assembly have pbd files, diagnostic JSON will contain information about
// line number and column number
var stackTrace = new StackTrace(true);
var stackFrames = stackTrace.GetFrames();
SetStacktraceInformation(stackFrames, true);

if (Exception == null)
{
return;
}
// add stack trace from exception
var head = DiagnosticStack.Any() ? DiagnosticStack[0] : null;
var generatedStack = Exception.GetExceptionStackFrames(head);
SetStacktraceInformation(generatedStack, false);
}

private void SetStacktraceInformation(StackFrame[] stackFrames, bool includeCallingAssembly, int startingIndex = 0)
{
// check if stack frames exists
if (stackFrames == null)
{
return;
}
var executedAssemblyName = Assembly.GetExecutingAssembly().FullName;
//if callingAssemblyFound is true, we dont need to found calling assembly in current stacktrace
//if includeCallingAssembly is true, we dont need to found calling assembly in current stacktrace
bool callingAssemblyFound = !includeCallingAssembly;

foreach (var stackFrame in stackFrames)
{
var method = stackFrame.GetMethod();
var declaringType = method?.DeclaringType;
if (declaringType == null)
if (stackFrame == null)
{
//received invalid or unvailable stackframe
continue;
}
Assembly assembly = declaringType.Assembly;
Assembly assembly = stackFrame?.GetMethod()?.DeclaringType?.Assembly;
if (assembly == null)
{
continue;
}
var assemblyName = assembly.FullName;
if (executedAssemblyName.Equals(assemblyName) && CallingAssembly == null)
if (executedAssemblyName.Equals(assemblyName))
{
// remove all system and microsoft stack frames
//if we add any stackframe to list this is mistake because we receive
//system or microsoft dll (for example async invoke)
DiagnosticStack.Clear();
ExceptionStack.Clear();
startingIndex = 0;
continue;
}

ExceptionStack.Insert(startingIndex, Model.JsonData.ExceptionStack.Convert(stackFrame, assembly.GetName().Name, true));
startingIndex++;
if (!callingAssemblyFound && !(SystemHelper.SystemAssembly(assembly)))
{
callingAssemblyFound = true;
CallingAssembly = assembly;
}
#if !NET35
//test if current stack frame is generated by async state machine
var declaringTypeInfo = declaringType.GetTypeInfo();
var stateMachineFrame = SystemHelper.StateMachineFrame(declaringTypeInfo);
if (stateMachineFrame)
{
continue;
}
#endif
if (!callingAssemblyFound)
{
continue;
}
var diagnosticStack = Model.JsonData.DiagnosticStack.Convert(stackFrame, assembly.GetName().Name, true);
DiagnosticStack.Insert(startingIndex, diagnosticStack);
startingIndex++;
}
}
/// <summary>
/// Set Calling Assembly and Exception Stack property.
/// CallingAssembly and StackTrace are necessary to prepare diagnostic JSON in BacktraceData class
/// </summary>
private void SetCallingAppInformation()
{
// generate stacktrace with file info
// if assembly have pbd files, diagnostic JSON will contain information about
// line number and column number
var stackTrace = new StackTrace(true);
var stackFrames = stackTrace.GetFrames();
SetStacktraceInformation(stackFrames, true);

if (Exception == null)
{
return;
}
//Add to current stack trace, stackframes from current exception
//stacktrace from current thread and from current excetpion are diffrent
var exceptionStackTrace = new StackTrace(Exception, true);
var exceptionStackFrames = exceptionStackTrace.GetFrames();
if (exceptionStackFrames != null && exceptionStackFrames[0] != null
&& ExceptionStack[0].ILOffset != exceptionStackFrames[0].GetILOffset()
&& ExceptionStack[0].FunctionName != exceptionStackFrames[0].GetMethod()?.Name)
{
SetStacktraceInformation(exceptionStackFrames, false);
}
}

}
}
57 changes: 57 additions & 0 deletions Backtrace/Common/StackTraceHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Backtrace.Common
{
/// <summary>
/// All usefull information about current StackTrace
/// </summary>
public static class StackTraceHelper
{
/// <summary>
/// Get current thread stack trace
/// </summary>
/// <param name="source">Current exception (if exists</param>
/// <returns>Current thread stack frames</returns>
public static List<StackFrame> GetStackFrames(Exception source = null)
{
var currentAssembly = Assembly.GetExecutingAssembly();
// generate stacktrace with file info
// if assembly have pbd files, diagnostic JSON will contain information about
// line number and column number
var stackTrace = new StackTrace(true);
#if DEBUG
Trace.WriteLine("CURRENT THREAD STACK TRACE:");
Trace.WriteLine(stackTrace.ToString());
Trace.WriteLine("END OF THE STACK TRACE");
#endif
var stackFrames = stackTrace.GetFrames()
.Where(n => n?.GetMethod()?.DeclaringType?.Assembly != currentAssembly)
.ToList();

if (source != null)
{
var exceptionStackTrace = new StackTrace(source, true);
#if DEBUG
Trace.WriteLine("CURRENT EXCEPTION STACK TRACE:");
Trace.WriteLine(exceptionStackTrace.ToString());
Trace.WriteLine("END OF THE EXCEPTION STACK TRACE");
#endif
var exceptionStackFrames = exceptionStackTrace.GetFrames();
//information from exception stack frame is already in current stacktrace (example: catching unhandled app exception)
if (stackFrames[0] != null && exceptionStackFrames[0] != null
&& stackFrames[0].GetILOffset() == exceptionStackFrames[0].GetILOffset()
&& stackFrames[0].GetMethod()?.Name == exceptionStackFrames[0].GetMethod()?.Name)
{
return stackFrames;
}
stackFrames.InsertRange(0, exceptionStackFrames);
}
return stackFrames;
}
}
}
11 changes: 1 addition & 10 deletions Backtrace/Common/SystemHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ internal static bool SystemAssembly(Assembly assembly)
{
return false;
}
var assemblyName = assembly.GetName().Name;
var assemblyName = assembly.FullName;
return SystemAssembly(assemblyName);
}
/// <summary>
Expand All @@ -143,16 +143,7 @@ internal static bool SystemAssembly(string assemblyName)
return false;
}
return (assemblyName.StartsWith("Microsoft.")
|| assemblyName.StartsWith("mscorlib")
|| assemblyName.Equals("System")
|| assemblyName.StartsWith("System."));
}
#if !NET35
internal static bool StateMachineFrame(TypeInfo declaringTypeInfo)
{
return typeof(System.Runtime.CompilerServices.IAsyncStateMachine)
.GetTypeInfo().IsAssignableFrom(declaringTypeInfo);
}
#endif
}
}
70 changes: 45 additions & 25 deletions Backtrace/Extensions/ExceptionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using Backtrace.Model;
using Backtrace.Model.JsonData;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;

Expand All @@ -22,35 +20,57 @@ public static BacktraceReport ToBacktraceReport(this Exception source)
{
return new BacktraceReport(source);
}
#if !NET35

/// Provides full stack trace for the exception that occurred.
/// </summary>
/// <param name="exception">Exception object.</param>
/// <param name="environmentStackTrace">Environment stack trace, for pulling additional stack frames.</param>
public static string ToLogString(this Exception exception, string environmentStackTrace)
{
List<string> environmentStackTraceLines = GetUserStackTraceLines(environmentStackTrace);
environmentStackTraceLines.RemoveAt(0);

List<string> stackTraceLines = GetStackTraceLines(exception.StackTrace);
stackTraceLines.AddRange(environmentStackTraceLines);

string fullStackTrace = String.Join(Environment.NewLine, stackTraceLines);

string logMessage = exception.Message + Environment.NewLine + fullStackTrace;
return logMessage;
}

/// <summary>
/// Generate stack traces that not exists in current thread stack trace
/// Gets a list of stack frame lines, as strings.
/// </summary>
/// <returns>Unique exception stack frames</returns>
internal static StackFrame[] GetExceptionStackFrames(this Exception source, DiagnosticStack firstFrame)
/// <param name="stackTrace">Stack trace string.</param>
private static List<string> GetStackTraceLines(string stackTrace)
{
if (source == null)
{
return null;
}
var exceptionStackTrace = new StackTrace(source, true);
var exceptionStackFrames = exceptionStackTrace.GetFrames();
if (exceptionStackFrames == null || !exceptionStackFrames.Any())
{
return null;
}
if (firstFrame == null)
{
return exceptionStackFrames;
}
var comparer = exceptionStackFrames[0];
//validate if exception stack frame exists in environment stack trace
if (firstFrame.ILOffset == comparer.GetILOffset()
&& firstFrame.FunctionName == comparer.GetMethod()?.Name)
return stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.None).ToList();
}

/// <summary>
/// Gets a list of stack frame lines, as strings, only including those for which line number is known.
/// </summary>
/// <param name="fullStackTrace">Full stack trace, including external code.</param>
private static List<string> GetUserStackTraceLines(string fullStackTrace)
{
List<string> outputList = new List<string>();
Regex regex = new Regex(@"([^\)]*\)) in (.*):line (\d)*$", RegexOptions.Compiled);

List<string> stackTraceLines = ExceptionExtensions.GetStackTraceLines(fullStackTrace);
foreach (string stackTraceLine in stackTraceLines)
{
return null;
if (!regex.IsMatch(stackTraceLine))
{
continue;
}

outputList.Add(stackTraceLine);
}
return exceptionStackFrames;

return outputList;
}
#endif
}
}
4 changes: 2 additions & 2 deletions Backtrace/Model/BacktraceData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ internal Dictionary<string, SourceCode> SourceCode
{
get
{
var sourceCode = new SourceCodeData(Report.DiagnosticStack);
var sourceCode = new SourceCodeData(Report.ExceptionStack);
if (sourceCode.data.Any())
{
return sourceCode.data;
Expand Down Expand Up @@ -199,7 +199,7 @@ public BacktraceData(BacktraceReportBase<T> report, Dictionary<string, T> scoped
{
Report = report;
_backtraceAttributes = new BacktraceAttributes<T>(Report, scopedAttributes);
ThreadData = new ThreadData(report.CallingAssembly, Report.DiagnosticStack);
ThreadData = new ThreadData(report.CallingAssembly, Report.ExceptionStack);
}
}
}
Loading

0 comments on commit aa49c5d

Please sign in to comment.