soygul / NBug

Automated bug reporting library for .NET
http://soygul.com/nbug
MIT License
191 stars 78 forks source link

Crash when exception report is empty #48

Closed vegardlarsen closed 9 years ago

vegardlarsen commented 9 years ago

I received the following crash reports from a customer (who sent it to me via e-mail). One zip file was empty, the other one contained this error:

<?xml version="1.0"?>
<SerializableException xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ExtendedInformation>
    <HResult>-2146233088</HResult>
  </ExtendedInformation>
  <InnerException>
    <ExtendedInformation>
      <HResult>-2146233079</HResult>
    </ExtendedInformation>
    <InnerException>
      <ExtendedInformation>
        <HResult>-2147467262</HResult>
      </ExtendedInformation>
      <Message>Xml type 'List of xdt:untypedAtomic' does not support a conversion from Clr type 'WebExceptionStatus' to Clr type 'String'.</Message>
      <Source>System.Xml</Source>
      <StackTrace>   at System.Xml.Schema.XmlListConverter.ChangeListType(Object value, Type destinationType, IXmlNamespaceResolver nsResolver)
   at System.Xml.Schema.XmlUntypedConverter.ToString(Object value, IXmlNamespaceResolver nsResolver)
   at System.Xml.XmlWriter.WriteValue(Object value)
   at NBug.Core.Util.Serialization.SerializableDictionary`2.WriteXml(XmlWriter writer)
   at System.Xml.Serialization.XmlSerializationWriter.WriteSerializable(IXmlSerializable serializable, String name, String ns, Boolean isNullable, Boolean wrapped)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterSerializableException.Write2_SerializableException(String n, String ns, SerializableException o, Boolean isNullable, Boolean needType)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterSerializableException.Write3_SerializableException(Object o)</StackTrace>
      <TargetSite>System.Object ChangeListType(System.Object, System.Type, System.Xml.IXmlNamespaceResolver) @ System.Xml.Schema.XmlListConverter</TargetSite>
      <Type>System.InvalidCastException</Type>
    </InnerException>
    <Message>There was an error generating the XML document.</Message>
    <Source>System.Xml</Source>
    <StackTrace>   at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
   at System.Xml.Serialization.XmlSerializer.Serialize(Stream stream, Object o, XmlSerializerNamespaces namespaces)
   at NBug.Core.Reporting.BugReport.CreateReportZip(SerializableException serializableException, Report report)
   at NBug.Core.Reporting.BugReport.Report(Exception exception, ExceptionThread exceptionThread)</StackTrace>
    <TargetSite>Void Serialize(System.Xml.XmlWriter, System.Object, System.Xml.Serialization.XmlSerializerNamespaces, System.String, System.String) @ System.Xml.Serialization.XmlSerializer</TargetSite>
    <Type>System.InvalidOperationException</Type>
  </InnerException>
  <Message>An exception occurred during bug report generation process. See the inner exception for details.</Message>
  <Source>NBug</Source>
  <StackTrace>   at NBug.Core.Util.Logging.Logger.Error(String message, Exception exception)
   at NBug.Core.Reporting.BugReport.Report(Exception exception, ExceptionThread exceptionThread)
   at NBug.Handler.ThreadExceptionHandler(Object sender, ThreadExceptionEventArgs e)
   at System.Windows.Forms.Application.ThreadContext.OnThreadException(Exception t)
   at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
   at System.Windows.Forms.Control.WndProc(Message&amp; m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)</StackTrace>
  <TargetSite>Void Error(System.String, System.Exception) @ NBug.Core.Util.Logging.Logger</TargetSite>
  <Type>NBug.Core.Util.Exceptions.NBugRuntimeException</Type>
</SerializableException>

I have a sneaking suspicion that the initial crash report tried to serialize something that was unserializable, but I haven't been able to confirm that. NBug should in any case probably handle this error better, so it doesn't bring down the whole app.

soygul commented 9 years ago

I'm assuming that this is an ASP.NET app, in which case all weird things are normal! i.e. they have used untypedAtomic integer for WebExceptionStatus!!!

NBug swallows all internal exception in a giant try/catch block when ReleaseMode=true so it can't crash the host app at all. It should be the actual exception crashing the app, and NBug just failing to generate a proper report..

vegardlarsen commented 9 years ago

Actually, this is a WinForms app. When NBug fails creating the report, the app crashes again. At least according to my customers, I haven't been able to reproduce this locally yet.

soygul commented 9 years ago

Here is the giant try/catch block: https://github.com/soygul/NBug/blob/master/NBug/Core/Reporting/BugReport.cs#L31

Unless it is a CLR bug, there is no way that an exception will escape a try/catch like that!

soygul commented 9 years ago

I found time to inspect this further and I can safely confirm that any exception during bug reporting/submission phases cannot bring down the application. Not only is there proper try/catch at the top level, but everything occurs in a background thread creating a double safety net.

However an internal NBug exception that occurs after an unhandled exception in your code will force the app to quit (which is what happens normally anyway, unless if you didn't change this CLR behavior for some reason).

var executionFlow = new BugReport().Report(e.Exception, ExceptionThread.UI_WinForms);
if (executionFlow == ExecutionFlow.BreakExecution)
{
    Environment.Exit(0);
}

...

        internal ExecutionFlow Report(Exception exception, ExceptionThread exceptionThread)
        {
            try
            {
                Logger.Trace("Starting to generate a bug report for the exception.");
                var serializableException = new SerializableException(exception);
                var report = new Report(serializableException);

                var handler = ProcessingException;
                if (handler != null)
                {
                    Logger.Trace("Notifying the user before handling the exception.");

                    // Allowing user to add any custom information to the report
                    handler(exception, report);
                }

                var uiDialogResult = UISelector.DisplayBugReportUI(exceptionThread, serializableException, report);
                if (uiDialogResult.Report == SendReport.Send)
                {
                    this.CreateReportZip(serializableException, report);
                }

                return uiDialogResult.Execution;
            }
            catch (Exception ex)
            {
                Logger.Error("An exception occurred during bug report generation process. See the inner exception for details.", ex);
                return ExecutionFlow.BreakExecution; // Since an internal exception occured
            }
        }
vegardlarsen commented 9 years ago

It turns out this exception happened in my application whenever the user was behind a proxy server or had no internet connection when using a WebClient to download content. However, whenever that exception was written to disk, NBug could not serialize it and showed the internal exception viewer. It would probably be a good idea to see if you could somehow get it to serialize the WebExceptionStatus enum.

soygul commented 9 years ago

Exceptions are not serializable normally so we have our custom serializable exception wrapper: https://github.com/soygul/NBug/blob/master/NBug/Core/Util/Serialization/SerializableException.cs

You can try and fix it there and make a PR. I'm not doing much C# these days so don't have VS installed so can't do it myself.

Closing this as the general solution is ReleaseMode=true until someone contributes.