1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- *
3 * Author: Michael Zucchi <notzed@ximian.com>
5 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
30 #ifdef HAVE_BACKTRACE_SYMBOLS
32 #ifdef HAVE_ELFUTILS_LIBDWFL
33 #include <elfutils/libdwfl.h>
39 #include "camel-debug.h"
41 gint camel_verbose_debug;
43 static GHashTable *debug_table = NULL;
51 * CAMEL_DEBUG is set to a comma separated list of modules to debug.
52 * The modules can contain module-specific specifiers after a ':', or
53 * just act as a wildcard for the module or even specifier. e.g. 'imap'
54 * for imap debug, or 'imap:folder' for imap folder debug. Additionaly,
55 * ':folder' can be used for a wildcard for any folder operations.
58 camel_debug_init (void)
62 d = g_strdup (getenv ("CAMEL_DEBUG"));
66 debug_table = g_hash_table_new (g_str_hash, g_str_equal);
69 while (*p && *p != ',')
73 g_hash_table_insert (debug_table, d, d);
77 if (g_hash_table_lookup (debug_table, "all"))
78 camel_verbose_debug = 1;
86 * Check to see if a debug mode is activated. @mode takes one of two forms,
87 * a fully qualified 'module:target', or a wildcard 'module' name. It
88 * returns a boolean to indicate if the module or module and target is
89 * currently activated for debug output.
93 gboolean camel_debug (const gchar *mode)
95 if (camel_verbose_debug)
102 if (g_hash_table_lookup (debug_table, mode))
105 /* Check for fully qualified debug */
106 colon = strchr (mode, ':');
108 fallback = g_alloca (strlen (mode) + 1);
109 strcpy (fallback, mode);
110 colon = (colon - mode) + fallback;
111 /* Now check 'module[:*]' */
113 if (g_hash_table_lookup (debug_table, fallback))
115 /* Now check ':subsystem' */
117 if (g_hash_table_lookup (debug_table, colon))
125 static GMutex debug_lock;
130 * Start debug output for a given mode, used to make sure debug output
131 * is output atomically and not interspersed with unrelated stuff.
133 * Returns: Returns true if mode is set, and in which case, you must
134 * call debug_end when finished any screen output.
137 camel_debug_start (const gchar *mode)
139 if (camel_debug (mode)) {
140 g_mutex_lock (&debug_lock);
141 printf ("Thread %p >\n", g_thread_self ());
151 * Call this when you're done with your debug output. If and only if
152 * you called camel_debug_start, and if it returns TRUE.
155 camel_debug_end (void)
157 printf ("< %p >\n", g_thread_self ());
158 g_mutex_unlock (&debug_lock);
162 #include <sys/debugreg.h>
165 i386_length_and_rw_bits (gint len,
166 enum target_hw_bp_type type)
178 case hw_read: /* x86 doesn't support data-read watchpoints */
183 case hw_io_access: /* not yet supported */
188 internal_error (__FILE__, __LINE__, "Invalid hw breakpoint type %d in i386_length_and_rw_bits.\n", (gint) type);
194 return (DR_LEN_1 | rw);
196 return (DR_LEN_2 | rw);
198 return (DR_LEN_4 | rw);
200 if (TARGET_HAS_DR_LEN_8)
201 return (DR_LEN_8 | rw);
203 internal_error (__FILE__, __LINE__, "Invalid hw breakpoint length %d in i386_length_and_rw_bits.\n", len);
207 #define I386_DR_SET_RW_LEN(i,rwlen) \
209 dr_control_mirror &= ~(0x0f << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * (i))); \
210 dr_control_mirror |= ((rwlen) << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * (i))); \
213 #define I386_DR_LOCAL_ENABLE(i) \
214 dr_control_mirror |= (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * (i)))
216 #define set_dr(regnum, val) \
217 __asm__("movl %0,%%db" #regnum \
221 #define get_dr(regnum, val) \
222 __asm__("movl %%db" #regnum ", %0" \
225 /* fine idea, but it doesn't work, crashes in get_dr :-/ */
227 camel_debug_hwatch (gint wp,
232 g_assert (wp <= DR_LASTADDR);
233 g_assert (sizeof (addr) == 4);
236 /* set watch mode + size */
237 rw = DR_RW_WRITE | DR_LEN_4;
238 control &= ~(((1 << DR_CONTROL_SIZE) - 1) << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * wp));
239 control |= rw << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * wp);
240 /* set watch enable */
241 control |= ( 1<< (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * wp));
242 control |= DR_LOCAL_SLOWDOWN;
243 control &= ~DR_CONTROL_RESERVED;
264 G_LOCK_DEFINE_STATIC (ptr_tracker);
265 static GHashTable *ptr_tracker = NULL;
274 free_pt_data (gpointer ptr)
276 struct pt_data *ptd = ptr;
283 g_string_free (ptd->backtrace, TRUE);
288 dump_left_ptrs_cb (gpointer key,
292 guint *left = user_data;
293 struct pt_data *ptd = value;
294 gboolean have_info = ptd && ptd->info;
295 gboolean have_bt = ptd && ptd->backtrace && ptd->backtrace->str && *ptd->backtrace->str;
298 g_print (" %p %s%s%s%s%s%s\n", key, have_info ? "(" : "", have_info ? ptd->info : "", have_info ? ")" : "", have_bt ? "\n" : "", have_bt ? ptd->backtrace->str : "", have_bt && *left > 0 ? "\n" : "");
301 #ifdef HAVE_BACKTRACE_SYMBOLS
303 by_backtrace_hash (gconstpointer ptr)
305 const struct pt_data *ptd = ptr;
307 if (!ptd || !ptd->backtrace)
310 return g_str_hash (ptd->backtrace->str);
314 by_backtrace_equal (gconstpointer ptr1,
317 const struct pt_data *ptd1 = ptr1, *ptd2 = ptr2;
319 if ((!ptd1 || !ptd1->backtrace) && (!ptd2 || !ptd2->backtrace))
322 return ptd1 && ptd1->backtrace && ptd2 && ptd2->backtrace && g_str_equal (ptd1->backtrace->str, ptd2->backtrace->str);
326 dump_by_backtrace_cb (gpointer key,
330 guint *left = user_data;
331 struct pt_data *ptd = key;
332 guint count = GPOINTER_TO_UINT (value);
335 dump_left_ptrs_cb (ptd->ptr, ptd, left);
337 gboolean have_info = ptd && ptd->info;
338 gboolean have_bt = ptd && ptd->backtrace && ptd->backtrace->str && *ptd->backtrace->str;
342 g_print (" %d x %s%s%s%s%s%s\n", count, have_info ? "(" : "", have_info ? ptd->info : "", have_info ? ")" : "", have_bt ? "\n" : "", have_bt ? ptd->backtrace->str : "", have_bt && *left > 0 ? "\n" : "");
347 dump_by_backtrace (GHashTable *ptrs)
349 GHashTable *by_bt = g_hash_table_new (by_backtrace_hash, by_backtrace_equal);
352 struct ptr_data *ptd;
355 g_hash_table_iter_init (&iter, ptrs);
356 while (g_hash_table_iter_next (&iter, &key, &value)) {
363 cnt = GPOINTER_TO_UINT (g_hash_table_lookup (by_bt, ptd));
366 g_hash_table_insert (by_bt, ptd, GUINT_TO_POINTER (cnt));
369 count = g_hash_table_size (by_bt);
370 g_hash_table_foreach (by_bt, dump_by_backtrace_cb, &count);
371 g_hash_table_destroy (by_bt);
373 #endif /* HAVE_BACKTRACE_SYMBOLS */
376 dump_tracked_ptrs (gboolean is_at_exit)
378 G_LOCK (ptr_tracker);
381 g_print ("\n----------------------------------------------------------\n");
382 if (g_hash_table_size (ptr_tracker) == 0) {
383 g_print (" All tracked pointers were properly removed\n");
385 guint count = g_hash_table_size (ptr_tracker);
386 g_print (" Left %d tracked pointers:\n", count);
387 #ifdef HAVE_BACKTRACE_SYMBOLS
388 dump_by_backtrace (ptr_tracker);
390 g_hash_table_foreach (ptr_tracker, dump_left_ptrs_cb, &count);
393 g_print ("----------------------------------------------------------\n");
394 } else if (!is_at_exit) {
395 g_print ("\n----------------------------------------------------------\n");
396 g_print (" Did not track any pointers yet\n");
397 g_print ("----------------------------------------------------------\n");
400 G_UNLOCK (ptr_tracker);
403 #ifdef HAVE_BACKTRACE_SYMBOLS
405 #ifdef HAVE_ELFUTILS_LIBDWFL
407 dwfl_get (gboolean reload)
409 static gchar *debuginfo_path = NULL;
410 static Dwfl *dwfl = NULL;
411 static gboolean checked_for_dwfl = FALSE;
412 static GMutex dwfl_mutex;
413 static const Dwfl_Callbacks proc_callbacks = {
414 .find_debuginfo = dwfl_standard_find_debuginfo,
415 .debuginfo_path = &debuginfo_path,
416 .find_elf = dwfl_linux_proc_find_elf
419 g_mutex_lock (&dwfl_mutex);
421 if (checked_for_dwfl) {
423 g_mutex_unlock (&dwfl_mutex);
431 checked_for_dwfl = TRUE;
433 dwfl = dwfl_begin (&proc_callbacks);
435 g_mutex_unlock (&dwfl_mutex);
440 if (dwfl_linux_proc_report (dwfl, getpid ()) != 0 || dwfl_report_end (dwfl, NULL, NULL) != 0) {
445 g_mutex_unlock (&dwfl_mutex);
450 struct getmodules_callback_arg
453 const gchar *func_name;
454 const gchar *file_path;
459 getmodules_callback (Dwfl_Module *module,
460 gpointer *module_userdata_pointer,
461 const gchar *module_name,
462 Dwarf_Addr module_low_addr,
465 struct getmodules_callback_arg *arg = arg_voidp;
468 arg->func_name = dwfl_module_addrname (module, (GElf_Addr) arg->addr);
469 line = dwfl_module_getsrc (module, (GElf_Addr) arg->addr);
471 arg->file_path = dwfl_lineinfo (line, NULL, &arg->lineno, NULL, NULL, NULL);
473 arg->file_path = NULL;
476 return arg->func_name ? DWARF_CB_ABORT : DWARF_CB_OK;
478 #endif /* HAVE_ELFUTILS_LIBDWFL */
481 addr_lookup (gpointer addr,
482 const gchar **file_path,
484 const gchar *fallback)
486 #ifdef HAVE_ELFUTILS_LIBDWFL
487 Dwfl *dwfl = dwfl_get (FALSE);
488 struct getmodules_callback_arg arg;
494 arg.func_name = NULL;
495 arg.file_path = NULL;
498 dwfl_getmodules (dwfl, getmodules_callback, &arg, 0);
500 if (!arg.func_name && fallback && strstr (fallback, "/lib") != fallback && strstr (fallback, "/usr/lib") != fallback) {
501 dwfl = dwfl_get (TRUE);
503 dwfl_getmodules (dwfl, getmodules_callback, &arg, 0);
506 *file_path = arg.file_path;
507 *lineno = arg.lineno;
509 return arg.func_name;
510 #else /* HAVE_ELFUTILS_LIBDWFL */
512 #endif /* HAVE_ELFUTILS_LIBDWFL */
515 #endif /* HAVE_BACKTRACE_SYMBOLS */
518 get_current_backtrace (void)
520 #ifdef HAVE_BACKTRACE_SYMBOLS
521 #define MAX_BT_DEPTH 50
523 gpointer bt[MAX_BT_DEPTH + 1];
527 nptrs = backtrace (bt, MAX_BT_DEPTH + 1);
531 bt_syms = backtrace_symbols (bt, nptrs);
535 bt_str = g_string_new ("");
536 for (ii = 2; ii < nptrs; ii++) {
538 const gchar *file_path = NULL;
539 const gchar *str = addr_lookup (bt[ii], &file_path, &lineno, bt_syms[ii]);
549 g_string_append (bt_str, "\n\t by ");
550 g_string_append (bt_str, str);
551 if (str != bt_syms[ii])
552 g_string_append (bt_str, "()");
554 if (file_path && lineno > 0) {
555 const gchar *lastsep = strrchr (file_path, G_DIR_SEPARATOR);
556 g_string_append_printf (bt_str, " at %s:%d", lastsep ? lastsep + 1 : file_path, lineno);
562 if (bt_str->len == 0) {
563 g_string_free (bt_str, TRUE);
566 g_string_insert (bt_str, 0, "\t at ");
572 #else /* HAVE_BACKTRACE_SYMBOLS */
574 #endif /* HAVE_BACKTRACE_SYMBOLS */
578 dump_left_at_exit_cb (void)
580 dump_tracked_ptrs (TRUE);
582 G_LOCK (ptr_tracker);
584 g_hash_table_destroy (ptr_tracker);
587 G_UNLOCK (ptr_tracker);
591 * camel_pointer_tracker_track_with_info:
592 * @ptr: pointer to add to the pointer tracker
593 * @info: info to print in tracker summary
595 * Adds pointer to the pointer tracker, with associated information,
596 * which is printed in summary of pointer tracker printed by
597 * camel_pointer_tracker_dump(). For convenience can be used
598 * camel_pointer_tracker_track(), which adds place of the caller
599 * as @info. Added pointer should be removed with pair function
600 * camel_pointer_tracker_untrack().
605 camel_pointer_tracker_track_with_info (gpointer ptr,
610 g_return_if_fail (ptr != NULL);
612 G_LOCK (ptr_tracker);
614 ptr_tracker = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, free_pt_data);
615 atexit (dump_left_at_exit_cb);
618 ptd = g_new0 (struct pt_data, 1);
620 ptd->info = g_strdup (info);
621 ptd->backtrace = get_current_backtrace ();
623 g_hash_table_insert (ptr_tracker, ptr, ptd);
625 G_UNLOCK (ptr_tracker);
629 * camel_pointer_tracker_untrack:
630 * @ptr: pointer to remove from the tracker
632 * Removes pointer from the pointer tracker. It's an error to try
633 * to remove pointer which was not added to the tracker by
634 * camel_pointer_tracker_track() or camel_pointer_tracker_track_with_info(),
635 * or a pointer which was already removed.
640 camel_pointer_tracker_untrack (gpointer ptr)
642 g_return_if_fail (ptr != NULL);
644 G_LOCK (ptr_tracker);
647 g_printerr ("Pointer tracker not initialized, thus cannot remove %p\n", ptr);
648 else if (!g_hash_table_lookup (ptr_tracker, ptr))
649 g_printerr ("Pointer %p is not tracked\n", ptr);
651 g_hash_table_remove (ptr_tracker, ptr);
653 G_UNLOCK (ptr_tracker);
657 * camel_pointer_tracker_dump:
659 * Prints information about currently stored pointers
660 * in the pointer tracker. This is called automatically
661 * on application exit if camel_pointer_tracker_track() or
662 * camel_pointer_tracker_track_with_info() was called.
664 * Note: If the library is configured with --enable-backtraces,
665 * then also backtraces where the pointer was added is printed
671 camel_pointer_tracker_dump (void)
673 dump_tracked_ptrs (FALSE);