1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * lt-error.c
4 * Copyright (C) 2011-2012 Akira TAGOH
5 *
6 * Authors:
7 * Akira TAGOH <akira@tagoh.org>
8 *
9 * You may distribute under the terms of either the GNU
10 * Lesser General Public License or the Mozilla Public
11 * License, as specified in the README file.
12 */
13 #ifdef HAVE_CONFIG_H
14 #include "config.h"
15 #endif
16
17 #ifdef HAVE_EXECINFO_H
18 #include <execinfo.h>
19 #endif
20 #include <stdlib.h>
21 #include "lt-list.h"
22 #include "lt-mem.h"
23 #include "lt-messages.h"
24 #include "lt-utils.h"
25 #include "lt-error.h"
26
27 struct _lt_error_t {
28 lt_mem_t parent;
29 lt_list_t *data;
30 };
31 typedef struct _lt_error_data_t {
32 lt_mem_t parent;
33 lt_error_type_t type;
34 char *message;
35 char **traces;
36 size_t stack_size;
37 } lt_error_data_t;
38
39 /**
40 * SECTION:lt-error
41 * @Short_Description: Error handling
42 * @Title: Error
43 *
44 * This section describes the error handling in this library.
45 */
46 /*< private >*/
47
48 /**
49 * lt_error_new:
50 *
51 * Creates #lt_error_t object. this function is protected and not supposed
52 * to use in applications directly. Use lt_error_set().
53 *
54 * Returns: (transfer full): a newly allocated #lt_error_t. it has to be freed
55 * with lt_error_unref().
56 */
57 lt_error_t *
lt_error_new(void)58 lt_error_new(void)
59 {
60 return lt_mem_alloc_object(sizeof (lt_error_t));
61 }
62
63 /*< public >*/
64
65 /**
66 * lt_error_ref:
67 * @error: a #lt_error_t
68 *
69 * Inscreases the reference count of @error.
70 *
71 * Returns: (transfer none): the same @error object.
72 */
73 lt_error_t *
lt_error_ref(lt_error_t * error)74 lt_error_ref(lt_error_t *error)
75 {
76 lt_return_val_if_fail (error != NULL, NULL);
77
78 return lt_mem_ref(&error->parent);
79 }
80
81 /**
82 * lt_error_unref:
83 * @error: a #lt_error_t
84 *
85 * Decreases the reference count of @error. when its reference count
86 * drops to 0, the object is finalized (i.e. its memory is freed).
87 */
88 void
lt_error_unref(lt_error_t * error)89 lt_error_unref(lt_error_t *error)
90 {
91 if (error)
92 lt_mem_unref(&error->parent);
93 }
94
95 /**
96 * lt_error_set:
97 * @error: a return location for a #lt_error_t
98 * @type: a #lt_error_type_t
99 * @message: the string format to output the error messages
100 * @...: the parameters to insert into the format string
101 *
102 * Sets the error into @error according to the given parameters.
103 *
104 * Returns: an instance of #lt_error_t
105 */
106 lt_error_t *
lt_error_set(lt_error_t ** error,lt_error_type_t type,const char * message,...)107 lt_error_set(lt_error_t **error,
108 lt_error_type_t type,
109 const char *message,
110 ...)
111 {
112 va_list ap;
113 #if HAVE_BACKTRACE
114 void *traces[1024];
115 #endif
116 lt_error_data_t *d;
117 int size = 0;
118 lt_bool_t allocated;
119
120 lt_return_val_if_fail (error != NULL, NULL);
121
122 d = lt_mem_alloc_object(sizeof (lt_error_data_t));
123 if (!d)
124 goto bail0;
125 if (!*error)
126 *error = lt_error_new();
127 if (!*error) {
128 lt_mem_unref(&d->parent);
129 goto bail0;
130 }
131
132 d->type = type;
133 va_start(ap, message);
134 d->message = lt_strdup_vprintf(message, ap);
135 va_end(ap);
136
137 #if HAVE_BACKTRACE
138 size = backtrace(traces, 1024);
139 if (size > 0)
140 d->traces = backtrace_symbols(traces, size);
141 #else
142 d->traces = NULL;
143 #endif
144 d->stack_size = size;
145
146 lt_mem_add_ref(&d->parent, d->message, free);
147 if (d->traces)
148 lt_mem_add_ref(&d->parent, d->traces, free);
149
150 allocated = (*error)->data == NULL;
151 (*error)->data = lt_list_append((*error)->data, d, (lt_destroy_func_t)lt_mem_unref);
152 if (allocated)
153 lt_mem_add_ref(&(*error)->parent,
154 (*error)->data,
155 (lt_destroy_func_t)lt_list_free);
156
157 return *error;
158 bail0:
159 lt_critical("Out of memory");
160
161 return *error;
162 }
163
164 /**
165 * lt_error_clear:
166 * @error: a #lt_error_t
167 *
168 * Clean up all of the errors in @error.
169 */
170 void
lt_error_clear(lt_error_t * error)171 lt_error_clear(lt_error_t *error)
172 {
173 if (error) {
174 if (error->data)
175 lt_mem_delete_ref(&error->parent, error->data);
176 error->data = NULL;
177 }
178 }
179
180 /**
181 * lt_error_is_set:
182 * @error: a #lt_error_t
183 * @type: a #lt_error_type_t
184 *
185 * Checks if @error contains @type of errors. if #LT_ERR_ANY is set to @type,
186 * all the types of the errors are targeted. otherwise the result is filtered
187 * out by @type.
188 *
189 * Returns: %TRUE if any, otherwise %FALSE
190 */
191 lt_bool_t
lt_error_is_set(lt_error_t * error,lt_error_type_t type)192 lt_error_is_set(lt_error_t *error,
193 lt_error_type_t type)
194 {
195 if (type == LT_ERR_ANY) {
196 return error && error->data;
197 } else {
198 if (error && error->data) {
199 lt_list_t *l;
200
201 for (l = error->data; l != NULL; l = lt_list_next(l)) {
202 lt_error_data_t *d = lt_list_value(l);
203
204 if (d->type == type)
205 return TRUE;
206 }
207 }
208 }
209
210 return FALSE;
211 }
212
213 /**
214 * lt_error_print:
215 * @error: a #lt_error_t
216 * @type: a #lt_error_type_t
217 *
218 * Output the error messages in @error according to @type.
219 */
220 void
lt_error_print(lt_error_t * error,lt_error_type_t type)221 lt_error_print(lt_error_t *error,
222 lt_error_type_t type)
223 {
224 lt_list_t *l;
225
226 if (lt_error_is_set(error, type)) {
227 lt_warning("Error raised:");
228 for (l = error->data; l != NULL; l = lt_list_next(l)) {
229 lt_error_data_t *d = lt_list_value(l);
230 int i;
231
232 if (type == LT_ERR_ANY || type == d->type) {
233 lt_warning(" %s", d->message);
234 if (d->stack_size > 0) {
235 lt_warning(" Backtraces:");
236 } else {
237 lt_warning(" No backtraces");
238 }
239 for (i = 1; i < d->stack_size; i++) {
240 lt_warning(" %d. %s", i - 1, d->traces[i]);
241 }
242 }
243 }
244 }
245 }
246