Patchwork [5/8] Introduce QError

login
register
mail settings
Submitter Luiz Capitulino
Date Nov. 4, 2009, 8:04 p.m.
Message ID <1257365047-25895-6-git-send-email-lcapitulino@redhat.com>
Download mbox | patch
Permalink /patch/37617/
State New
Headers show

Comments

Luiz Capitulino - Nov. 4, 2009, 8:04 p.m.
QError is a high-level data type which represents an exception,
it stores the following error information:

- name           A generic error name (eg. "ServiceUnavailable")
- description    A detailed error description, which may contain
                 references to run-time error data
- filename       The file name of where the error occurred
- line number    The exact line number of the error
- run-time data  Run-time error data

The qerror_print() function should be used to properly format and
print the stored information to the right place, that is, to stderr
if the Monitor is not running, or to the Monitor's device otherwise.

The following functions are exported:

- qerror_new(): Create a new QError
- qerror_from_info(): Create a new QError from the specified error
                      information
- qerror_print(): Print the specified QError

Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
---
 Makefile  |    2 +-
 qerror.c  |  235 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qerror.h  |   39 ++++++++++
 qobject.h |    1 +
 4 files changed, 276 insertions(+), 1 deletions(-)
 create mode 100644 qerror.c
 create mode 100644 qerror.h
Paolo Bonzini - Nov. 5, 2009, 9:34 a.m.
On 11/04/2009 09:04 PM, Luiz Capitulino wrote:
> +#define QERR_DEV_NFOUND "DeviceNotFound"
> +#define QERR_SER_UNAV   "ServiceUnavailable"

I don't like packing names this way (what's wrong with DEVICE_NOT_FOUND 
and SERVICE_UNAVAILABLE?...), but otherwise the series looks fine.

Paolo

Patch

diff --git a/Makefile b/Makefile
index 6fcbcaa..2dfaebd 100644
--- a/Makefile
+++ b/Makefile
@@ -135,7 +135,7 @@  obj-y += buffered_file.o migration.o migration-tcp.o qemu-sockets.o
 obj-y += qemu-char.o aio.o savevm.o
 obj-y += msmouse.o ps2.o
 obj-y += qdev.o qdev-properties.o
-obj-y += qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o qjson.o
+obj-y += qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o qjson.o qerror.o
 obj-y += qemu-config.o
 
 obj-$(CONFIG_BRLAPI) += baum.o
diff --git a/qerror.c b/qerror.c
new file mode 100644
index 0000000..7d3137f
--- /dev/null
+++ b/qerror.c
@@ -0,0 +1,235 @@ 
+/*
+ * QError: QEMU Error data-type.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+#include "qint.h"
+#include "qjson.h"
+#include "qerror.h"
+#include "qstring.h"
+#include "sysemu.h"
+#include "qemu-common.h"
+
+static void qerror_destroy_obj(QObject *obj);
+
+static const QType qerror_type = {
+    .code = QTYPE_QERROR,
+    .destroy = qerror_destroy_obj,
+};
+
+/**
+ * qerror_new(): Create a new QError
+ *
+ * Return strong reference.
+ */
+QError *qerror_new(void)
+{
+    QError *qerr;
+
+    qerr = qemu_mallocz(sizeof(*qerr));
+    QOBJECT_INIT(qerr, &qerror_type);
+
+    return qerr;
+}
+
+/**
+ * qerror_from_info(): Create a new QError from error information
+ *
+ * The information consists of:
+ *
+ * - name    generic error name
+ * - file    the file name of where the error occurred
+ * - linenr  the line number of where the error occurred
+ * - desc    detailed description (see below)
+ * - fmt     JSON printf-like format for 'specific data'
+ * - va      va_list of all arguments for 'specific data'
+ *
+ * Note that this is a low-level function, it is supposed to be called
+ * by higher-level functions or macros.
+ *
+ * The 'desc' parameter is a printf-like string, the format of the format
+ * string is:
+ *
+ * %(KEY)TYPE
+ *
+ * Where KEY is a QDict key and TYPE is the type of its value, KEY and
+ * its value must be passed to qerror_from_info().
+ *
+ * Valid types are:
+ *
+ * s  (string)
+ * d  (integer)
+ *
+ * Example:
+ *
+ * "foo error on device: %(device)s slot: %(slot_nr)d"
+ *
+ * A single percent sign can be printed if followed by a second one,
+ * for example:
+ *
+ * "running out of foo: %(foo)d%%"
+ *
+ * Return strong reference.
+ */
+QError *qerror_from_info(const char *name, const char *file, int linenr,
+                         const char *desc, const char *fmt, va_list *va)
+{
+    QError *qerr;
+
+    qerr = qerror_new();
+    qerr->name = name;
+    qerr->file = file;
+    qerr->linenr = linenr;
+    qerr->desc = desc;
+    if (fmt) {
+        qerr->data = qobject_from_json_va(fmt, va);
+        assert(qerr->data != NULL);
+    }
+
+    return qerr;
+}
+
+static char *get_substr(const char *start, const char *end)
+{
+    char *str;
+    size_t length;
+
+    length = end - start + 1;
+    str = qemu_malloc(length + 1);
+    memcpy(str, start, length);
+    str[length] = '\0';
+
+    return str;
+}
+
+static void qerror_abort(const QError *qerr)
+{
+    fprintf(stderr, " in '%s' at %s:%d\n", qerr->desc, qerr->file,qerr->linenr);
+    abort();
+}
+
+#define ERROR_PREFIX "\n\nqerror: "
+
+static void type_error(const QError *qerr, int c)
+{
+    fprintf(stderr, ERROR_PREFIX "invalid type '%c'", c);
+    qerror_abort(qerr);
+}
+
+static void key_error(const QError *qerr, const char *key)
+{
+    fprintf(stderr, ERROR_PREFIX "key '%s' not found in QDict ", key);
+    fprintf(stderr, "call at %s:%d\n", qerr->file, qerr->linenr);
+    abort();
+}
+
+static void parse_error(const QError *qerror, int c)
+{
+    fprintf(stderr, ERROR_PREFIX "expected '%c'", c);
+    qerror_abort(qerror);
+}
+
+static const char *append_field(QString *qstring, const QError *qerror,
+                                const char *start)
+{
+    int type;
+    char *name;
+    QDict *qdict;
+    const char *end;
+
+    if (*start != '%')
+        parse_error(qerror, '%');
+    start++;
+    if (*start != '(')
+        parse_error(qerror, '(');
+    start++;
+
+    end = strchr(start, ')');
+    if (!end)
+        parse_error(qerror, ')');
+
+    name = get_substr(start, end - 1);
+    qdict = qobject_to_qdict(qerror->data);
+
+    if (!qdict_haskey(qdict, name)) {
+        key_error(qerror, name);
+    }
+
+    type = *++end;
+    switch (type) {
+        case 's':
+            qstring_append(qstring, qdict_get_str(qdict, name));
+            break;
+        case 'd':
+            qstring_append_int(qstring, qdict_get_int(qdict, name));
+            break;
+        default:
+            type_error(qerror, type);
+    }
+
+    qemu_free(name);
+    return ++end;
+}
+
+/**
+ * qerror_print(): Print QError data
+ *
+ * This function will print the member 'desc' of the specified QError object,
+ * it uses qemu_error() for this, so that the output is routed to the right
+ * place (ie. stderr ou Monitor's device).
+ */
+void qerror_print(const QError *qerror)
+{
+    const char *p;
+    QString *qstring;
+
+    assert(qerror->desc != NULL);
+
+    qstring = qstring_new();
+
+    for (p = qerror->desc; *p != '\0';) {
+        if (*p != '%') {
+            qstring_append_chr(qstring, *p++);
+        } else if (*(p + 1) == '%') {
+            qstring_append_chr(qstring, '%');
+            p += 2;
+        } else {
+            p = append_field(qstring, qerror, p);
+        }
+    }
+
+    qemu_error("%s\n", qstring_get_str(qstring));
+    QDECREF(qstring);
+}
+
+/**
+ * qobject_to_qerror(): Convert a QObject into a QError
+ */
+QError *qobject_to_qerror(const QObject *obj)
+{
+    if (qobject_type(obj) != QTYPE_QERROR) {
+        return NULL;
+    }
+
+    return container_of(obj, QError, base);
+}
+
+/**
+ * qerror_destroy_obj(): Free all memory allocated by a QError
+ */
+static void qerror_destroy_obj(QObject *obj)
+{
+    QError *qerr;
+
+    assert(obj != NULL);
+    qerr = qobject_to_qerror(obj);
+
+    qobject_decref(qerr->data);
+    qemu_free(qerr);
+}
diff --git a/qerror.h b/qerror.h
new file mode 100644
index 0000000..dc39dec
--- /dev/null
+++ b/qerror.h
@@ -0,0 +1,39 @@ 
+/*
+ * QError header file.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+#ifndef QERROR_H
+#define QERROR_H
+
+#include <stdarg.h>
+#include "qobject.h"
+
+typedef struct QError {
+    QObject_HEAD;
+    const char *name;   /* generic name */
+    const char *desc;   /* description */
+    int linenr;         /* line number */
+    const char *file;   /* file name */
+    QObject *data;      /* run-time data */
+} QError;
+
+QError *qerror_new(void);
+QError *qerror_from_info(const char *name, const char *file, int linenr,
+                         const char *desc, const char *fmt, va_list *va);
+void qerror_print(const QError *qerror);
+QError *qobject_to_qerror(const QObject *obj);
+
+/*
+ * Common error names
+ */
+#define QERR_DEV_NFOUND "DeviceNotFound"
+#define QERR_SER_UNAV   "ServiceUnavailable"
+
+#endif /* QERROR_H */
diff --git a/qobject.h b/qobject.h
index 167b607..838dddc 100644
--- a/qobject.h
+++ b/qobject.h
@@ -43,6 +43,7 @@  typedef enum {
     QTYPE_QLIST,
     QTYPE_QFLOAT,
     QTYPE_QBOOL,
+    QTYPE_QERROR,
 } qtype_code;
 
 struct QObject;