Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[platform/upstream/evolution-data-server.git] / camel / camel-debug.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- *
2  *
3  * Author: Michael Zucchi <notzed@ximian.com>
4  *
5  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
6  *
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.
10  *
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.
15  *
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
19  * USA
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #ifdef HAVE_BACKTRACE_SYMBOLS
31 #include <execinfo.h>
32 #ifdef HAVE_ELFUTILS_LIBDWFL
33 #include <elfutils/libdwfl.h>
34 #include <errno.h>
35 #include <unistd.h>
36 #endif
37 #endif
38
39 #include "camel-debug.h"
40
41 gint camel_verbose_debug;
42
43 static GHashTable *debug_table = NULL;
44
45 /**
46  * camel_debug_init:
47  * @void:
48  *
49  * Init camel debug.
50  *
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.
56  **/
57 void
58 camel_debug_init (void)
59 {
60         gchar *d;
61
62         d = g_strdup (getenv ("CAMEL_DEBUG"));
63         if (d) {
64                 gchar *p;
65
66                 debug_table = g_hash_table_new (g_str_hash, g_str_equal);
67                 p = d;
68                 while (*p) {
69                         while (*p && *p != ',')
70                                 p++;
71                         if (*p)
72                                 *p++ = 0;
73                         g_hash_table_insert (debug_table, d, d);
74                         d = p;
75                 }
76
77                 if (g_hash_table_lookup (debug_table, "all"))
78                         camel_verbose_debug = 1;
79         }
80 }
81
82 /**
83  * camel_debug:
84  * @mode:
85  *
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.
90  *
91  * Returns:
92  **/
93 gboolean camel_debug (const gchar *mode)
94 {
95         if (camel_verbose_debug)
96                 return TRUE;
97
98         if (debug_table) {
99                 gchar *colon;
100                 gchar *fallback;
101
102                 if (g_hash_table_lookup (debug_table, mode))
103                         return TRUE;
104
105                 /* Check for fully qualified debug */
106                 colon = strchr (mode, ':');
107                 if (colon) {
108                         fallback = g_alloca (strlen (mode) + 1);
109                         strcpy (fallback, mode);
110                         colon = (colon - mode) + fallback;
111                         /* Now check 'module[:*]' */
112                         *colon = 0;
113                         if (g_hash_table_lookup (debug_table, fallback))
114                                 return TRUE;
115                         /* Now check ':subsystem' */
116                         *colon = ':';
117                         if (g_hash_table_lookup (debug_table, colon))
118                                 return TRUE;
119                 }
120         }
121
122         return FALSE;
123 }
124
125 static GMutex debug_lock;
126 /**
127  * camel_debug_start:
128  * @mode:
129  *
130  * Start debug output for a given mode, used to make sure debug output
131  * is output atomically and not interspersed with unrelated stuff.
132  *
133  * Returns: Returns true if mode is set, and in which case, you must
134  * call debug_end when finished any screen output.
135  **/
136 gboolean
137 camel_debug_start (const gchar *mode)
138 {
139         if (camel_debug (mode)) {
140                 g_mutex_lock (&debug_lock);
141                 printf ("Thread %p >\n", g_thread_self ());
142                 return TRUE;
143         }
144
145         return FALSE;
146 }
147
148 /**
149  * camel_debug_end:
150  *
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.
153  **/
154 void
155 camel_debug_end (void)
156 {
157         printf ("< %p >\n", g_thread_self ());
158         g_mutex_unlock (&debug_lock);
159 }
160
161 #if 0
162 #include <sys/debugreg.h>
163
164 static unsigned
165 i386_length_and_rw_bits (gint len,
166                          enum target_hw_bp_type type)
167 {
168   unsigned rw;
169
170   switch (type)
171     {
172       case hw_execute:
173         rw = DR_RW_EXECUTE;
174         break;
175       case hw_write:
176         rw = DR_RW_WRITE;
177         break;
178       case hw_read:      /* x86 doesn't support data-read watchpoints */
179       case hw_access:
180         rw = DR_RW_READ;
181         break;
182 #if 0
183       case hw_io_access: /* not yet supported */
184         rw = DR_RW_IORW;
185         break;
186 #endif
187       default:
188         internal_error (__FILE__, __LINE__, "Invalid hw breakpoint type %d in i386_length_and_rw_bits.\n", (gint) type);
189     }
190
191   switch (len)
192     {
193       case 1:
194         return (DR_LEN_1 | rw);
195       case 2:
196         return (DR_LEN_2 | rw);
197       case 4:
198         return (DR_LEN_4 | rw);
199       case 8:
200         if (TARGET_HAS_DR_LEN_8)
201           return (DR_LEN_8 | rw);
202       default:
203         internal_error (__FILE__, __LINE__, "Invalid hw breakpoint length %d in i386_length_and_rw_bits.\n", len);
204     }
205 }
206
207 #define I386_DR_SET_RW_LEN(i,rwlen) \
208   do { \
209     dr_control_mirror &= ~(0x0f << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * (i)));   \
210     dr_control_mirror |= ((rwlen) << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * (i))); \
211   } while (0)
212
213 #define I386_DR_LOCAL_ENABLE(i) \
214   dr_control_mirror |= (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * (i)))
215
216 #define set_dr(regnum, val) \
217                 __asm__("movl %0,%%db" #regnum  \
218                         : /* no output */ \
219                         :"r" (val))
220
221 #define get_dr(regnum, val) \
222                 __asm__("movl %%db" #regnum ", %0"  \
223                         :"=r" (val))
224
225 /* fine idea, but it doesn't work, crashes in get_dr :-/ */
226 void
227 camel_debug_hwatch (gint wp,
228                     gpointer addr)
229 {
230      guint32 control, rw;
231
232      g_assert (wp <= DR_LASTADDR);
233      g_assert (sizeof (addr) == 4);
234
235      get_dr (7, control);
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;
244
245      switch (wp) {
246      case 0:
247              set_dr (0, addr);
248              break;
249      case 1:
250              set_dr (1, addr);
251              break;
252      case 2:
253              set_dr (2, addr);
254              break;
255      case 3:
256              set_dr (3, addr);
257              break;
258      }
259      set_dr (7, control);
260 }
261
262 #endif
263
264 G_LOCK_DEFINE_STATIC (ptr_tracker);
265 static GHashTable *ptr_tracker = NULL;
266
267 struct pt_data {
268         gpointer ptr;
269         gchar *info;
270         GString *backtrace;
271 };
272
273 static void
274 free_pt_data (gpointer ptr)
275 {
276         struct pt_data *ptd = ptr;
277
278         if (!ptd)
279                 return;
280
281         g_free (ptd->info);
282         if (ptd->backtrace)
283                 g_string_free (ptd->backtrace, TRUE);
284         g_free (ptd);
285 }
286
287 static void
288 dump_left_ptrs_cb (gpointer key,
289                    gpointer value,
290                    gpointer user_data)
291 {
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;
296
297         *left = (*left) - 1;
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" : "");
299 }
300
301 #ifdef HAVE_BACKTRACE_SYMBOLS
302 static guint
303 by_backtrace_hash (gconstpointer ptr)
304 {
305         const struct pt_data *ptd = ptr;
306
307         if (!ptd || !ptd->backtrace)
308                 return 0;
309
310         return g_str_hash (ptd->backtrace->str);
311 }
312
313 static gboolean
314 by_backtrace_equal (gconstpointer ptr1,
315                     gconstpointer ptr2)
316 {
317         const struct pt_data *ptd1 = ptr1, *ptd2 = ptr2;
318
319         if ((!ptd1 || !ptd1->backtrace) && (!ptd2 || !ptd2->backtrace))
320                 return TRUE;
321
322         return ptd1 && ptd1->backtrace && ptd2 && ptd2->backtrace && g_str_equal (ptd1->backtrace->str, ptd2->backtrace->str);
323 }
324
325 static void
326 dump_by_backtrace_cb (gpointer key,
327                       gpointer value,
328                       gpointer user_data)
329 {
330         guint *left = user_data;
331         struct pt_data *ptd = key;
332         guint count = GPOINTER_TO_UINT (value);
333
334         if (count == 1) {
335                 dump_left_ptrs_cb (ptd->ptr, ptd, left);
336         } else {
337                 gboolean have_info = ptd && ptd->info;
338                 gboolean have_bt = ptd && ptd->backtrace && ptd->backtrace->str && *ptd->backtrace->str;
339
340                 *left = (*left) - 1;
341
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" : "");
343         }
344 }
345
346 static void
347 dump_by_backtrace (GHashTable *ptrs)
348 {
349         GHashTable *by_bt = g_hash_table_new (by_backtrace_hash, by_backtrace_equal);
350         GHashTableIter iter;
351         gpointer key, value;
352         struct ptr_data *ptd;
353         guint count;
354
355         g_hash_table_iter_init (&iter, ptrs);
356         while (g_hash_table_iter_next (&iter, &key, &value)) {
357                 guint cnt;
358
359                 ptd = value;
360                 if (!ptd)
361                         continue;
362
363                 cnt = GPOINTER_TO_UINT (g_hash_table_lookup (by_bt, ptd));
364                 cnt++;
365
366                 g_hash_table_insert (by_bt, ptd, GUINT_TO_POINTER (cnt));
367         }
368
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);
372 }
373 #endif /* HAVE_BACKTRACE_SYMBOLS */
374
375 static void
376 dump_tracked_ptrs (gboolean is_at_exit)
377 {
378         G_LOCK (ptr_tracker);
379
380         if (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");
384                 } else {
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);
389                         #else
390                         g_hash_table_foreach (ptr_tracker, dump_left_ptrs_cb, &count);
391                         #endif
392                 }
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");
398         }
399
400         G_UNLOCK (ptr_tracker);
401 }
402
403 #ifdef HAVE_BACKTRACE_SYMBOLS
404
405 #ifdef HAVE_ELFUTILS_LIBDWFL
406 static Dwfl *
407 dwfl_get (gboolean reload)
408 {
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
417         };
418
419         g_mutex_lock (&dwfl_mutex);
420
421         if (checked_for_dwfl) {
422                 if (!reload) {
423                         g_mutex_unlock (&dwfl_mutex);
424                         return dwfl;
425                 }
426
427                 dwfl_end (dwfl);
428                 dwfl = NULL;
429         }
430
431         checked_for_dwfl = TRUE;
432
433         dwfl = dwfl_begin (&proc_callbacks);
434         if (!dwfl) {
435                 g_mutex_unlock (&dwfl_mutex);
436                 return NULL;
437         }
438
439         errno = 0;
440         if (dwfl_linux_proc_report (dwfl, getpid ()) != 0 || dwfl_report_end (dwfl, NULL, NULL) != 0) {
441                 dwfl_end (dwfl);
442                 dwfl = NULL;
443         }
444
445         g_mutex_unlock (&dwfl_mutex);
446
447         return dwfl;
448 }
449
450 struct getmodules_callback_arg
451 {
452         gpointer addr;
453         const gchar *func_name;
454         const gchar *file_path;
455         gint lineno;
456 };
457
458 static gint
459 getmodules_callback (Dwfl_Module *module,
460                      gpointer *module_userdata_pointer,
461                      const gchar *module_name,
462                      Dwarf_Addr module_low_addr,
463                      gpointer arg_voidp)
464 {
465         struct getmodules_callback_arg *arg = arg_voidp;
466         Dwfl_Line *line;
467
468         arg->func_name = dwfl_module_addrname (module, (GElf_Addr) arg->addr);
469         line = dwfl_module_getsrc (module, (GElf_Addr) arg->addr);
470         if (line) {
471                 arg->file_path = dwfl_lineinfo (line, NULL, &arg->lineno, NULL, NULL, NULL);
472         } else {
473                 arg->file_path = NULL;
474         }
475
476         return arg->func_name ? DWARF_CB_ABORT : DWARF_CB_OK;
477 }
478 #endif /* HAVE_ELFUTILS_LIBDWFL */
479
480 static const gchar *
481 addr_lookup (gpointer addr,
482              const gchar **file_path,
483              gint *lineno,
484              const gchar *fallback)
485 {
486 #ifdef HAVE_ELFUTILS_LIBDWFL
487         Dwfl *dwfl = dwfl_get (FALSE);
488         struct getmodules_callback_arg arg;
489
490         if (!dwfl)
491                 return NULL;
492
493         arg.addr = addr;
494         arg.func_name = NULL;
495         arg.file_path = NULL;
496         arg.lineno = -1;
497
498         dwfl_getmodules (dwfl, getmodules_callback, &arg, 0);
499
500         if (!arg.func_name && fallback && strstr (fallback, "/lib") != fallback && strstr (fallback, "/usr/lib") != fallback) {
501                 dwfl = dwfl_get (TRUE);
502                 if (dwfl)
503                         dwfl_getmodules (dwfl, getmodules_callback, &arg, 0);
504         }
505
506         *file_path = arg.file_path;
507         *lineno = arg.lineno;
508
509         return arg.func_name;
510 #else /* HAVE_ELFUTILS_LIBDWFL */
511         return NULL;
512 #endif /* HAVE_ELFUTILS_LIBDWFL */
513 }
514
515 #endif /* HAVE_BACKTRACE_SYMBOLS */
516
517 static GString *
518 get_current_backtrace (void)
519 {
520 #ifdef HAVE_BACKTRACE_SYMBOLS
521         #define MAX_BT_DEPTH 50
522         gint nptrs, ii;
523         gpointer bt[MAX_BT_DEPTH + 1];
524         gchar **bt_syms;
525         GString *bt_str;
526
527         nptrs = backtrace (bt, MAX_BT_DEPTH + 1);
528         if (nptrs <= 2)
529                 return NULL;
530
531         bt_syms = backtrace_symbols (bt, nptrs);
532         if (!bt_syms)
533                 return NULL;
534
535         bt_str = g_string_new ("");
536         for (ii = 2; ii < nptrs; ii++) {
537                 gint lineno = -1;
538                 const gchar *file_path = NULL;
539                 const gchar *str = addr_lookup (bt[ii], &file_path, &lineno, bt_syms[ii]);
540                 if (!str) {
541                         str = bt_syms[ii];
542                         file_path = NULL;
543                         lineno = -1;
544                 }
545                 if (!str)
546                         continue;
547
548                 if (bt_str->len)
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, "()");
553
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);
557                 }
558         }
559
560         g_free (bt_syms);
561
562         if (bt_str->len == 0) {
563                 g_string_free (bt_str, TRUE);
564                 bt_str = NULL;
565         } else {
566                 g_string_insert (bt_str, 0, "\t   at ");
567         }
568
569         return bt_str;
570
571         #undef MAX_BT_DEPTH
572 #else /* HAVE_BACKTRACE_SYMBOLS */
573         return NULL;
574 #endif /* HAVE_BACKTRACE_SYMBOLS */
575 }
576
577 static void
578 dump_left_at_exit_cb (void)
579 {
580         dump_tracked_ptrs (TRUE);
581
582         G_LOCK (ptr_tracker);
583         if (ptr_tracker) {
584                 g_hash_table_destroy (ptr_tracker);
585                 ptr_tracker = NULL;
586         }
587         G_UNLOCK (ptr_tracker);
588 }
589
590 /**
591  * camel_pointer_tracker_track_with_info:
592  * @ptr: pointer to add to the pointer tracker
593  * @info: info to print in tracker summary
594  *
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().
601  *
602  * Since: 3.6
603  **/
604 void
605 camel_pointer_tracker_track_with_info (gpointer ptr,
606                                        const gchar *info)
607 {
608         struct pt_data *ptd;
609
610         g_return_if_fail (ptr != NULL);
611
612         G_LOCK (ptr_tracker);
613         if (!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);
616         }
617
618         ptd = g_new0 (struct pt_data, 1);
619         ptd->ptr = ptr;
620         ptd->info = g_strdup (info);
621         ptd->backtrace = get_current_backtrace ();
622
623         g_hash_table_insert (ptr_tracker, ptr, ptd);
624
625         G_UNLOCK (ptr_tracker);
626 }
627
628 /**
629  * camel_pointer_tracker_untrack:
630  * @ptr: pointer to remove from the tracker
631  *
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.
636  *
637  * Since: 3.6
638  **/
639 void
640 camel_pointer_tracker_untrack (gpointer ptr)
641 {
642         g_return_if_fail (ptr != NULL);
643
644         G_LOCK (ptr_tracker);
645
646         if (!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);
650         else
651                 g_hash_table_remove (ptr_tracker, ptr);
652
653         G_UNLOCK (ptr_tracker);
654 }
655
656 /**
657  * camel_pointer_tracker_dump:
658  *
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.
663  *
664  * Note: If the library is configured with --enable-backtraces,
665  * then also backtraces where the pointer was added is printed
666  * in the summary.
667  *
668  * Since: 3.6
669  **/
670 void
671 camel_pointer_tracker_dump (void)
672 {
673         dump_tracked_ptrs (FALSE);
674 }