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.
This commit is contained in:
Michael James Gratton 2017-11-18 15:25:28 +11:00
parent 0d1efc2fe0
commit bcca75f5a8
7 changed files with 150 additions and 17 deletions

View file

@ -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:

View file

@ -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
}
}

2
debian/control vendored
View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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. */

View file

@ -118,6 +118,15 @@
<property name="top_attach">1</property>
</packing>
</child>
<child>
<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="shadow_type">in</property>
<child>
<object class="GtkTextView" id="detail_text">
<property name="visible">True</property>
@ -133,6 +142,8 @@
<property name="cursor_visible">False</property>
<property name="monospace">True</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>