[geary/wip/713006-better-error-reporting] Include a back trace in problem report technical details.



commit bcca75f5a827f30767f2d7e531724a2aa7238c25
Author: Michael James Gratton <mike vee net>
Date:   Sat Nov 18 15:25:28 2017 +1100

    Include a back trace in problem report technical details.
    
    This adds a dependcy on libunwind for generating the back trace.
    
    * src/CMakeLists.txt: Require libunwind-generic package and libunwind
      VAPI. Update docs and debian/control with new dependencies.
    
    * src/engine/api/geary-problem-report.vala (ProblemReport): Generate a
      stack trace in the default constructor if an error is specified.
    
    * src/client/components/main-window-info-bar.vala
      (MainWindowInfoBar::format_details): Include stack trafe from problem
      report in output if present.
    
    * ui/main-window-info-bar.ui: Add a ScrolledWindow around the TextView
      since the details could now be quite large.
    
    * bindings/vapi/libunwind.vapi: Add bindings for libunwind courtesy
      Guillaume Poirier-Morency, add Error enum.

 INSTALL                                         |    6 ++-
 bindings/vapi/libunwind.vapi                    |   63 +++++++++++++++++++++++
 debian/control                                  |    2 +
 src/CMakeLists.txt                              |    2 +
 src/client/components/main-window-info-bar.vala |   18 ++++--
 src/engine/api/geary-problem-report.vala        |   47 +++++++++++++++++
 ui/main-window-info-bar.ui                      |   29 +++++++---
 7 files changed, 150 insertions(+), 17 deletions(-)
---
diff --git a/INSTALL b/INSTALL
index 44962ec..da17adc 100644
--- a/INSTALL
+++ b/INSTALL
@@ -46,6 +46,7 @@
       * webkit2gtk-4.0
       * gcr-3
       * enchant
+      * libunwind
       * messaging-menu (optional; enables support for Ubuntu Unity
         messaging menu)
       * unity (optional; enables support for Ubuntu Unity launcher)
@@ -67,7 +68,7 @@
             desktop-file-utils gnome-doc-utils libcanberra-devel libgee-devel \
             glib2-devel gmime-devel gtk3-devel libnotify-devel sqlite-devel \
             webkitgtk4-devel libsecret-devel libxml2-devel vala-tools \
-            gcr-devel enchant-devel
+            gcr-devel enchant-devel libunwind-devel
 
 
   * Installing dependencies on Ubuntu/Debian
@@ -86,7 +87,8 @@
             cmake desktop-file-utils gnome-doc-utils libcanberra-dev \
             libgee-0.8-dev libglib2.0-dev libgmime-2.6-dev libgtk-3-dev \
             libsecret-1-dev libxml2-dev libnotify-dev libsqlite3-dev \
-            libwebkit2gtk-4.0-dev libgcr-3-dev libenchant-dev
+            libwebkit2gtk-4.0-dev libgcr-3-dev libenchant-dev \
+            libunwind-dev
 
     And for Ubuntu Unity integration:
 
diff --git a/bindings/vapi/libunwind.vapi b/bindings/vapi/libunwind.vapi
new file mode 100644
index 0000000..7a4b39d
--- /dev/null
+++ b/bindings/vapi/libunwind.vapi
@@ -0,0 +1,63 @@
+/*
+ * Based on version from Sentry-GLib: https://github.com/arteymix/sentry-glib
+ * Courtesy of Guillaume Poirier-Morency <guillaumepoiriermorency gmail com>
+ */
+
+[CCode (cprefix = "UNW_", lower_case_cprefix = "unw_", cheader_filename = "libunwind.h")]
+namespace Unwind
+{
+
+       [CCode (cname = "unw_context_t")]
+       public struct Context
+       {
+               [CCode (cname = "unw_getcontext")]
+               public Context ();
+       }
+
+       [CCode (cname = "unw_proc_info_t")]
+       public struct ProcInfo
+       {
+               void* start_ip;
+               void* end_ip;
+               void* lsda;
+               void* handler;
+               void* gp;
+               long flags;
+               int format;
+       }
+
+       [CCode (cname = "unw_frame_regnum_t")]
+       public enum Reg
+       {
+               IP,
+               SP,
+               EH
+       }
+
+       [CCode (cname = "unw_cursor_t", cprefix = "unw_")]
+       public struct Cursor
+       {
+               public Cursor.local (Context ctx);
+               public int get_proc_info (out ProcInfo pip);
+               public int get_proc_name (uint8[] bufp, out long offp = null);
+               public int get_reg (Reg reg, out void* valp);
+               public int step ();
+       }
+
+    [CCode (cname = "unw_error_t", cprefix = "UNW_E", has_type_id = false)]
+    public enum Error
+    {
+        SUCCESS,
+        UNSPEC,
+        NOMEM,
+        BADREG,
+        READONLYREG,
+        STOPUNWIND,
+        INVALIDIP,
+        BADFRAME,
+        INVAL,
+        BADVERSION,
+        NOINFO
+    }
+
+}
diff --git a/debian/control b/debian/control
index 61ffd31..61db4e6 100644
--- a/debian/control
+++ b/debian/control
@@ -23,6 +23,7 @@ Build-Depends: debhelper (>= 8),
  gnome-doc-utils,
  libgcr-3-dev (>= 3.10.1),
  libenchant-dev (>= 1.6.0)
+ libunwind8-dev (>= 1.1)
 Standards-Version: 3.8.3
 Homepage: https://wiki.gnome.org/Apps/Geary
 
@@ -45,6 +46,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends},
  libgcr-base-3-1 (>= 3.10.1),
  libgcr-ui-3-1 (>= 3.10.1),
  libenchant1c2a (>= 1.6.0)
+ libunwind8 (>= 1.1)
 Description: Email application
  Geary is an email application built around conversations, for the
  GNOME 3 desktop. It allows you to read, find and send email with a
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0f2a2e5..942c6b1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -512,6 +512,7 @@ pkg_check_modules(DEPS REQUIRED
     webkit2gtk-web-extension-4.0>=${TARGET_WEBKIT}
     javascriptcoregtk-4.0>=${TARGET_WEBKIT}
     enchant>=1.6
+    libunwind-generic>=1.1
     ${EXTRA_CLIENT_PKG_CONFIG}
 )
 
@@ -529,6 +530,7 @@ set(ENGINE_PACKAGES
   gio-2.0
   glib-2.0
   gmime-2.6
+  libunwind
   javascriptcore-4.0
   libxml-2.0
   posix
diff --git a/src/client/components/main-window-info-bar.vala b/src/client/components/main-window-info-bar.vala
index 13048f9..96ba5fb 100644
--- a/src/client/components/main-window-info-bar.vala
+++ b/src/client/components/main-window-info-bar.vala
@@ -213,12 +213,18 @@ public class MainWindowInfoBar : Gtk.InfoBar {
                 "Endpoint: %s\n", service_report.endpoint.to_string()
             );
         }
-        details.append_printf(
-            "Error type: %s\n", (this.report.error != null) ? this.report.format_error_type() : "None 
specified"
-        );
-        details.append_printf(
-            "Message: %s\n", (this.report.error != null) ? this.report.error.message : "None specified"
-        );
+        if (this.report.error == null) {
+            details.append("No error reported");
+        } else {
+            details.append_printf("Error type: %s\n", this.report.format_error_type());
+            details.append_printf("Message: %s\n", this.report.error.message);
+        }
+        if (this.report.backtrace != null) {
+            details.append("Back trace:\n");
+            foreach (Geary.ProblemReport.StackFrame frame in this.report.backtrace) {
+                details.append_printf(" - %s\n", frame.to_string());
+            }
+        }
         return details.str;
     }
 
diff --git a/src/engine/api/geary-problem-report.vala b/src/engine/api/geary-problem-report.vala
index dd9ad2e..938fd4e 100644
--- a/src/engine/api/geary-problem-report.vala
+++ b/src/engine/api/geary-problem-report.vala
@@ -9,6 +9,7 @@
 /** Describes available problem types. */
 public enum Geary.ProblemType {
 
+
     /** Indicates an engine problem not covered by one of the other types. */
     GENERIC_ERROR,
 
@@ -30,6 +31,7 @@ public enum Geary.ProblemType {
     /** Indicates an outgoing message was sent, but not saved. */
     SEND_EMAIL_SAVE_FAILED;
 
+
     /** Determines the appropriate problem type for an IOError. */
     public static ProblemType for_ioerror(IOError error) {
         if (error is IOError.CONNECTION_REFUSED ||
@@ -54,16 +56,61 @@ public enum Geary.ProblemType {
  */
 public class Geary.ProblemReport : Object {
 
+
+    /**
+     * Represents an individual stack frame in a call back-trace.
+     */
+    public class StackFrame {
+
+
+        /** Name of the function being called. */
+        public string name = "unknown";
+
+
+        internal StackFrame(Unwind.Cursor frame) {
+            uint8 proc_name[256];
+            int ret = -frame.get_proc_name(proc_name);
+                       if (ret == Unwind.Error.SUCCESS ||
+                ret == Unwind.Error.NOMEM) {
+                this.name = (string) proc_name;
+            }
+        }
+
+        public string to_string() {
+            return this.name;
+        }
+
+    }
+
+
     /** Describes the type of being reported. */
     public ProblemType problem_type { get; private set; }
 
     /** The exception caused the problem, if any. */
     public Error? error { get; private set; default = null; }
 
+    /** A back trace from when the problem report was constructed. */
+    public Gee.List<StackFrame>? backtrace = null;
+
 
     public ProblemReport(ProblemType type, Error? error) {
         this.problem_type = type;
         this.error = error;
+
+        if (error != null) {
+            // Some kind of exception occurred, so build a trace. This
+            // is far from perfect, but at least we will know where it
+            // was getting caught.
+            this.backtrace = new Gee.LinkedList<StackFrame>();
+            Unwind.Context trace = Unwind.Context();
+            Unwind.Cursor cursor = Unwind.Cursor.local(trace);
+
+            // This misses the first frame, but that's this
+            // constructor call, so we don't really care.
+            while (cursor.step() != 0) {
+                this.backtrace.add(new StackFrame(cursor));
+            }
+        }
     }
 
     /** Returns a string representation of the report, for debugging only. */
diff --git a/ui/main-window-info-bar.ui b/ui/main-window-info-bar.ui
index 312ffaa..6817927 100644
--- a/ui/main-window-info-bar.ui
+++ b/ui/main-window-info-bar.ui
@@ -119,19 +119,30 @@
       </packing>
     </child>
     <child>
-      <object class="GtkTextView" id="detail_text">
+      <object class="GtkScrolledWindow">
+        <property name="width_request">600</property>
+        <property name="height_request">200</property>
         <property name="visible">True</property>
         <property name="can_focus">True</property>
         <property name="hexpand">True</property>
         <property name="vexpand">True</property>
-        <property name="editable">False</property>
-        <property name="wrap_mode">word</property>
-        <property name="left_margin">6</property>
-        <property name="right_margin">6</property>
-        <property name="top_margin">6</property>
-        <property name="bottom_margin">6</property>
-        <property name="cursor_visible">False</property>
-        <property name="monospace">True</property>
+        <property name="shadow_type">in</property>
+        <child>
+          <object class="GtkTextView" id="detail_text">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="hexpand">True</property>
+            <property name="vexpand">True</property>
+            <property name="editable">False</property>
+            <property name="wrap_mode">word</property>
+            <property name="left_margin">6</property>
+            <property name="right_margin">6</property>
+            <property name="top_margin">6</property>
+            <property name="bottom_margin">6</property>
+            <property name="cursor_visible">False</property>
+            <property name="monospace">True</property>
+          </object>
+        </child>
       </object>
       <packing>
         <property name="left_attach">0</property>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]