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