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.
222 lines
6.7 KiB
Vala
222 lines
6.7 KiB
Vala
/*
|
|
* Copyright 2016 Software Freedom Conservancy Inc.
|
|
* Copyright 2017 Michael Gratton <mike@vee.net>
|
|
*
|
|
* This software is licensed under the GNU Lesser General Public License
|
|
* (version 2.1 or later). See the COPYING file in this distribution.
|
|
*/
|
|
|
|
/** Describes available problem types. */
|
|
public enum Geary.ProblemType {
|
|
|
|
|
|
/** Indicates an engine problem not covered by one of the other types. */
|
|
GENERIC_ERROR,
|
|
|
|
/** Indicates an error opening, using or closing the account database. */
|
|
DATABASE_FAILURE,
|
|
|
|
/** Indicates a problem establishing a connection. */
|
|
CONNECTION_ERROR,
|
|
|
|
/** Indicates a problem caused by a network operation. */
|
|
NETWORK_ERROR,
|
|
|
|
/** Indicates a non-network related server error. */
|
|
SERVER_ERROR,
|
|
|
|
/** Indicates credentials supplied for authentication were rejected. */
|
|
LOGIN_FAILED,
|
|
|
|
/** 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 ||
|
|
error is IOError.HOST_NOT_FOUND ||
|
|
error is IOError.HOST_UNREACHABLE ||
|
|
error is IOError.NETWORK_UNREACHABLE) {
|
|
return ProblemType.CONNECTION_ERROR;
|
|
}
|
|
|
|
if (error is IOError.CONNECTION_CLOSED ||
|
|
error is IOError.NOT_CONNECTED) {
|
|
return ProblemType.NETWORK_ERROR;
|
|
}
|
|
|
|
return ProblemType.GENERIC_ERROR;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Describes a error that the engine encountered, for reporting to the client.
|
|
*/
|
|
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. */
|
|
public string to_string() {
|
|
return "%s: %s".printf(
|
|
this.problem_type.to_string(),
|
|
format_full_error() ?? "no error reported"
|
|
);
|
|
}
|
|
|
|
/** Returns a string representation of the error type, for debugging. */
|
|
public string? format_error_type() {
|
|
string type = null;
|
|
if (this.error != null) {
|
|
const string QUARK_SUFFIX = "-quark";
|
|
string ugly_domain = this.error.domain.to_string();
|
|
if (ugly_domain.has_suffix(QUARK_SUFFIX)) {
|
|
ugly_domain = ugly_domain.substring(
|
|
0, ugly_domain.length - QUARK_SUFFIX.length
|
|
);
|
|
}
|
|
StringBuilder nice_domain = new StringBuilder();
|
|
string separator = (ugly_domain.index_of("_") != -1) ? "_" : "-";
|
|
foreach (string part in ugly_domain.split(separator)) {
|
|
if (part.length > 0) {
|
|
if (part == "io") {
|
|
nice_domain.append("IO");
|
|
} else {
|
|
nice_domain.append(part.up(1));
|
|
nice_domain.append(part.substring(1));
|
|
}
|
|
}
|
|
}
|
|
|
|
type = "%s %i".printf(nice_domain.str, this.error.code);
|
|
}
|
|
return type;
|
|
}
|
|
|
|
/** Returns a string representation of the complete error, for debugging. */
|
|
public string? format_full_error() {
|
|
string error = null;
|
|
if (this.error != null) {
|
|
error = String.is_empty(this.error.message)
|
|
? "%s: no message specified".printf(format_error_type())
|
|
: "%s: \"%s\"".printf(format_error_type(), this.error.message);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Describes an account-related error that the engine encountered.
|
|
*/
|
|
public class Geary.AccountProblemReport : ProblemReport {
|
|
|
|
|
|
/** The account related to the problem report. */
|
|
public AccountInformation account { get; private set; }
|
|
|
|
|
|
public AccountProblemReport(ProblemType type, AccountInformation account, Error? error) {
|
|
base(type, error);
|
|
this.account = account;
|
|
}
|
|
|
|
/** Returns a string representation of the report, for debugging only. */
|
|
public new string to_string() {
|
|
return "%s: %s".printf(this.account.id, base.to_string());
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Describes a service-related error that the engine encountered.
|
|
*/
|
|
public class Geary.ServiceProblemReport : AccountProblemReport {
|
|
|
|
|
|
/** The service related to the problem report. */
|
|
public Service service_type { get; private set; }
|
|
|
|
/** The endpoint for the report's service type. */
|
|
public Endpoint endpoint {
|
|
owned get {
|
|
return (this.service_type == Service.IMAP)
|
|
? this.account.get_imap_endpoint()
|
|
: this.account.get_smtp_endpoint();
|
|
}
|
|
}
|
|
|
|
|
|
public ServiceProblemReport(ProblemType type, AccountInformation account, Service service_type, Error? error) {
|
|
base(type, account, error);
|
|
this.service_type = service_type;
|
|
}
|
|
|
|
/** Returns a string representation of the report, for debugging only. */
|
|
public new string to_string() {
|
|
return "%s: %s: %s: %s".printf(
|
|
this.account.id,
|
|
this.service_type.to_string(),
|
|
this.problem_type.to_string(),
|
|
format_full_error() ?? "no error reported"
|
|
);
|
|
}
|
|
|
|
}
|