diff --git a/INSTALL b/INSTALL index 44962ecb..da17adce 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 00000000..7a4b39d6 --- /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 + */ + +[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 61ffd315..61db4e6f 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 0f2a2e5c..942c6b1a 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 13048f9c..96ba5fb0 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 dd9ad2ea..938fd4e3 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? 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(); + 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 312ffaaa..68179278 100644 --- a/ui/main-window-info-bar.ui +++ b/ui/main-window-info-bar.ui @@ -119,19 +119,30 @@ - + + 600 + 200 True True True True - False - word - 6 - 6 - 6 - 6 - False - True + in + + + True + True + True + True + False + word + 6 + 6 + 6 + 6 + False + True + + 0