From 4790202a07b9af48042749f746df270c4b5bd364 Mon Sep 17 00:00:00 2001 From: icnocop Date: Tue, 27 Feb 2018 22:20:30 -0800 Subject: [PATCH] Added support for generating and analyzing Silverlight 5 exception stack trace reports --- .nuget/BuildCommon.targets | 14 +-------- ...roductionStackTrace.Analyze.Console.csproj | 4 +++ .../ExceptionReportInterpreter.cs | 31 +++++++++++++------ .../ProductionStackTrace.Analyze.csproj | 6 +++- ProductionStackTrace.Analyze/SymbolSearch.cs | 31 ++++++++++++++++++- ProductionStackTrace.sln | 4 +-- ProductionStackTrace/ExceptionReporting.cs | 6 ++++ .../Internals/AssemblyDebugInfo.cs | 2 ++ ProductionStackTrace/Internals/PeHeaders.cs | 16 ++++++++++ .../ProductionStackTrace.csproj | 11 +++++-- .../ProductionStackTrace.nuspec | 1 + appveyor.yml | 11 +++++++ 12 files changed, 108 insertions(+), 29 deletions(-) create mode 100644 appveyor.yml diff --git a/.nuget/BuildCommon.targets b/.nuget/BuildCommon.targets index fcb1880..09059bc 100644 --- a/.nuget/BuildCommon.targets +++ b/.nuget/BuildCommon.targets @@ -19,24 +19,12 @@ TARGET_NET_20 TARGET_NET_40 + SILVERLIGHT $(DefineConstants); $(DefineConstants)$(CustomConstants) - - - $(ProjectDir)$(ProjectName).nuspec - true - - $(BuildDependsOn); - BuildPackage; - - - - diff --git a/ProductionStackTrace.Analyze.Console/ProductionStackTrace.Analyze.Console.csproj b/ProductionStackTrace.Analyze.Console/ProductionStackTrace.Analyze.Console.csproj index 1386d81..ab83e9c 100644 --- a/ProductionStackTrace.Analyze.Console/ProductionStackTrace.Analyze.Console.csproj +++ b/ProductionStackTrace.Analyze.Console/ProductionStackTrace.Analyze.Console.csproj @@ -67,8 +67,12 @@ --> + + $(ProjectDir)$(ProjectName).nuspec + + \ No newline at end of file diff --git a/ProductionStackTrace.Analyze/ExceptionReportInterpreter.cs b/ProductionStackTrace.Analyze/ExceptionReportInterpreter.cs index 7395f8e..6d5bab6 100644 --- a/ProductionStackTrace.Analyze/ExceptionReportInterpreter.cs +++ b/ProductionStackTrace.Analyze/ExceptionReportInterpreter.cs @@ -30,7 +30,7 @@ public class ExceptionReportInterpreter // MODULE: AssemblyName => AssemblyFullyQualifiedName; G:27657a27fg376787d6; A:1 private static readonly Regex s_regexAssemblyMapping = - new Regex(@"MODULE: (?[^\s]+(?=\s+\=>))\s+\=>\s+(?[^;]+)(;\s+(?[a-z]+\:[^;]+))+", RegexOptions.Singleline | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); + new Regex(@"MODULE: (?[^\s]+(?=\s+\=>))\s+\=>\s+(?[^;]+)(;(\s+)?(?[a-z]+\:[^;]+)?)+", RegexOptions.Singleline | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); private SymbolSearch _symSearch; @@ -101,6 +101,13 @@ public void Translate(TextReader r, TextWriter w) info.Attributes[kv[0]] = kv[1]; } + // PDB filename is derived from assembly name + '.pdb' extension + + var assemblyFileName = info.FullyQualifiedName; + int idxShortNameEnd = assemblyFileName.IndexOf(','); + if (idxShortNameEnd > 0) assemblyFileName = assemblyFileName.Substring(0, idxShortNameEnd); + var pdbFileName = assemblyFileName + ".pdb"; + // If GUID and Age are specified, that means that assembly has debug information // and the correspodning PDB file would have the matching GUID + Age attributes @@ -117,21 +124,25 @@ public void Translate(TextReader r, TextWriter w) info.PdbGuid = pdbGuid; info.fPdbSpecified = true; - // PDB filename is derived from assembly name + '.pdb' extension - - var assemblyFileName = info.FullyQualifiedName; - int idxShortNameEnd = assemblyFileName.IndexOf(','); - if (idxShortNameEnd > 0) assemblyFileName = assemblyFileName.Substring(0, idxShortNameEnd); - // Lookup PDB file using configured Symbol Search Paths. If found, then // load PDB symbol information - it will be used below to find source line numbers - info.PdbPath = _symSearch.FindPdbFile(assemblyFileName + ".pdb", info.PdbGuid, info.PdbAge); - if (info.PdbPath != null) - info.PdbSymbolLoader = SymbolLoader.Load(info.PdbPath); + info.PdbPath = _symSearch.FindPdbFile(pdbFileName, info.PdbGuid, info.PdbAge); + } + } + else + { + // try to search for it anyway + info.PdbPath = _symSearch.FindPdbFile(pdbFileName); + if (info.PdbPath != null) + { + info.fPdbSpecified = true; } } + if (info.PdbPath != null) + info.PdbSymbolLoader = SymbolLoader.Load(info.PdbPath); + mapping[assemblyName] = info; } } diff --git a/ProductionStackTrace.Analyze/ProductionStackTrace.Analyze.csproj b/ProductionStackTrace.Analyze/ProductionStackTrace.Analyze.csproj index fcf38cc..d17da2a 100644 --- a/ProductionStackTrace.Analyze/ProductionStackTrace.Analyze.csproj +++ b/ProductionStackTrace.Analyze/ProductionStackTrace.Analyze.csproj @@ -34,7 +34,7 @@ ..\Lib\dia2lib\net40\dia2lib.dll - True + False ..\Lib\dia2lib\net20\dia2lib.dll @@ -66,6 +66,9 @@ --> + + $(ProjectDir)$(ProjectName).nuspec + @@ -86,5 +89,6 @@ + \ No newline at end of file diff --git a/ProductionStackTrace.Analyze/SymbolSearch.cs b/ProductionStackTrace.Analyze/SymbolSearch.cs index 4ca51f1..fce44df 100644 --- a/ProductionStackTrace.Analyze/SymbolSearch.cs +++ b/ProductionStackTrace.Analyze/SymbolSearch.cs @@ -1,5 +1,7 @@ -using System; +using Dia2Lib; +using System; using System.Collections.Generic; +using System.IO; using System.Runtime.InteropServices; using System.Text; @@ -116,5 +118,32 @@ public string FindPdbFile(string pdbFileName, Guid guid, int age) return filePath.ToString(); } + + /// + /// Looks through the configured symbol paths to find a PDB symbol + /// file matching specified name. + /// + /// Name of the PDB file. + /// The pdb file path or null the pdf file wasn't found. + public string FindPdbFile(string pdbFileName) + { + foreach (string symbolPath in this.SymbolPaths) + { + string filePath = Path.Combine(symbolPath, pdbFileName); + if (File.Exists(filePath)) + { + DiaSourceClass dia = new DiaSourceClass(); + dia.loadDataFromPdb(filePath); + IDiaSession session; + dia.openSession(out session); + + IDiaSymbol symbol = session.globalScope; + + return FindPdbFile(pdbFileName, symbol.guid, (int)symbol.age); + } + } + + return null; + } } } diff --git a/ProductionStackTrace.sln b/ProductionStackTrace.sln index da822c6..a48a19b 100644 --- a/ProductionStackTrace.sln +++ b/ProductionStackTrace.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.21005.1 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProductionStackTrace", "ProductionStackTrace\ProductionStackTrace.csproj", "{6F8A759B-6096-468E-A370-09C9785D673C}" EndProject diff --git a/ProductionStackTrace/ExceptionReporting.cs b/ProductionStackTrace/ExceptionReporting.cs index 4bc2105..09bb1fb 100644 --- a/ProductionStackTrace/ExceptionReporting.cs +++ b/ProductionStackTrace/ExceptionReporting.cs @@ -38,6 +38,7 @@ public static string GetExceptionReport(Exception ex) { var info = ctx.AssemblyInfo[key]; builder.AppendFormat("MODULE: {0} => {1};", key, info.Assembly.FullName); +#if !SILVERLIGHT if (info.DebugInfo != null) { builder.AppendFormat(" G:{0:N}; A:{1}", info.DebugInfo.Guid, info.DebugInfo.Age); @@ -46,6 +47,7 @@ public static string GetExceptionReport(Exception ex) if (!string.Equals(pdbFileName, info.ShortName + ".pdb", StringComparison.OrdinalIgnoreCase)) builder.Append("; F:").Append(pdbFileName); } +#endif builder.AppendLine(); } } @@ -228,7 +230,9 @@ private static void AppendAssemblyName(StringBuilder builder, Assembly assembly, if (info == null) { ctx.AssemblyInfo.Add(assemblyName, info = new AssemblyReportInfo() { Assembly = assembly, ShortName = originalAssemblyName }); +#if !SILVERLIGHT info.DebugInfo = AssemblyDebugInfo.ReadAssemblyDebugInfo(assembly); +#endif } } @@ -243,7 +247,9 @@ private class ExceptionReportingContext private class AssemblyReportInfo { public Assembly Assembly; +#if !SILVERLIGHT public AssemblyDebugInfo DebugInfo; +#endif public string ShortName; } diff --git a/ProductionStackTrace/Internals/AssemblyDebugInfo.cs b/ProductionStackTrace/Internals/AssemblyDebugInfo.cs index 4d666fb..5578e08 100644 --- a/ProductionStackTrace/Internals/AssemblyDebugInfo.cs +++ b/ProductionStackTrace/Internals/AssemblyDebugInfo.cs @@ -18,6 +18,7 @@ private AssemblyDebugInfo() { } +#if !SILVERLIGHT /// /// Retrieve PDB information from Assembly, by reading it's PE header. /// @@ -88,6 +89,7 @@ public static AssemblyDebugInfo ReadAssemblyDebugInfo(Assembly assembly) Path = path }; } +#endif #region Struct definitions diff --git a/ProductionStackTrace/Internals/PeHeaders.cs b/ProductionStackTrace/Internals/PeHeaders.cs index 43a2173..3b5cbd7 100644 --- a/ProductionStackTrace/Internals/PeHeaders.cs +++ b/ProductionStackTrace/Internals/PeHeaders.cs @@ -416,11 +416,13 @@ private PeHeaders() { } +#if !SILVERLIGHT public static PeHeaders FromAssembly(Assembly assembly) { var modulePtr = Marshal.GetHINSTANCE(assembly.ManifestModule); return FromUnmanagedPtr(modulePtr); } +#endif public static PeHeaders FromUnmanagedPtr(IntPtr memoryPtr) { @@ -512,7 +514,12 @@ private static T FromBinaryReader(BinaryReader reader, byte[] lookahead) // Pin the managed memory while, copy it out the data, then unpin it GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); +#if SILVERLIGHT + T theStructure = default(T); + Marshal.PtrToStructure(handle.AddrOfPinnedObject(), theStructure); +#else T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); +#endif handle.Free(); return theStructure; @@ -520,7 +527,12 @@ private static T FromBinaryReader(BinaryReader reader, byte[] lookahead) private static T FromMemoryPtr(IntPtr memPtr, ref long index) { +#if SILVERLIGHT + T obj = default(T); + Marshal.PtrToStructure(new IntPtr(memPtr.ToInt64() + index), obj); +#else var obj = (T)Marshal.PtrToStructure(new IntPtr(memPtr.ToInt64() + index), typeof(T)); +#endif index += Marshal.SizeOf(typeof(T)); return obj; } @@ -594,7 +606,11 @@ public DateTime TimeStamp // Add in the number of seconds since 1970/1/1 returnValue = returnValue.AddSeconds(fileHeader.TimeDateStamp); // Adjust to local timezone +#if SILVERLIGHT + returnValue += TimeZoneInfo.Local.GetUtcOffset(returnValue); +#else returnValue += TimeZone.CurrentTimeZone.GetUtcOffset(returnValue); +#endif return returnValue; } diff --git a/ProductionStackTrace/ProductionStackTrace.csproj b/ProductionStackTrace/ProductionStackTrace.csproj index 15b085f..8104e16 100644 --- a/ProductionStackTrace/ProductionStackTrace.csproj +++ b/ProductionStackTrace/ProductionStackTrace.csproj @@ -11,7 +11,8 @@ ProductionStackTrace v4.5 512 - bin\$(Configuration)\$(TargetFrameworkVersion)\ + bin\$(Configuration)\$(TargetFrameworkVersion)\ + bin\$(Configuration)\sl5\ bin\$(Configuration) $(OutputPath)\$(AssemblyName).xml @@ -46,7 +47,8 @@ - + + + + $(ProjectDir)$(ProjectName).nuspec + + + \ No newline at end of file diff --git a/ProductionStackTrace/ProductionStackTrace.nuspec b/ProductionStackTrace/ProductionStackTrace.nuspec index 0f75a38..38c6f2b 100644 --- a/ProductionStackTrace/ProductionStackTrace.nuspec +++ b/ProductionStackTrace/ProductionStackTrace.nuspec @@ -23,6 +23,7 @@ + \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..ad7028d --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,11 @@ +version: 1.0.{build} +build_script: +- cmd: build.cmd package +test_script: +- cmd: >- + nuget install NUnit.Runners -Version 2.6.4 + + .\NUnit.Runners.2.6.4\tools\nunit-console-x86.exe .\ProductionStackTrace.Test\bin\Release\ProductionStackTrace.Test.dll +artifacts: +- path: _deploy\*.nupkg + name: NuGet Package \ No newline at end of file