/* * Copyright (C) 2003 Marco Pesenti Gritti * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Id$ */ #include "config.h" #include "ephy-debug.h" #include #ifdef HAVE_EXECINFO_H #include #endif #include #include #include #include static const char *ephy_debug_break = NULL; #ifndef DISABLE_PROFILING static GHashTable *ephy_profilers_hash = NULL; static char **ephy_profile_modules; static gboolean ephy_profile_all_modules; #endif /* !DISABLE_PROFILING */ #ifdef GNOME_ENABLE_DEBUG static char ** build_modules (const char *name, gboolean* is_all) { const char *env; *is_all = FALSE; env = g_getenv (name); if (env == NULL) return NULL; if (strcmp (env, "all") == 0) { *is_all = TRUE; return NULL; } return g_strsplit (g_getenv (name), ":", -1); } #endif #ifndef DISABLE_LOGGING static char **ephy_log_modules; static gboolean ephy_log_all_modules; static void log_module (const gchar *log_domain, GLogLevelFlags log_level, const char *message, gpointer user_data) { gboolean should_log = ephy_log_all_modules; if (!ephy_log_all_modules && !ephy_log_modules) return; if (ephy_log_all_modules) { guint i; for (i = 0; ephy_log_modules[i] != NULL; i++) { if (strstr (ephy_log_modules [i], message) != NULL) { should_log = TRUE; break; } } } if (should_log) { g_print ("%s\n", message); } } #endif /* !DISABLE_LOGGING */ #define MAX_DEPTH 200 static void trap_handler (const char *log_domain, GLogLevelFlags log_level, const char *message, gpointer user_data) { g_log_default_handler (log_domain, log_level, message, user_data); if (ephy_debug_break != NULL && (log_level & (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL))) { if (strcmp (ephy_debug_break, "suspend") == 0) { /* the suspend case is first because we wanna send the signal before * other threads have had a chance to get too far from the state that * caused this assertion (in case they happen to have been involved). */ g_print ("Suspending program; attach with the debugger.\n"); raise (SIGSTOP); } else if (strcmp (ephy_debug_break, "stack") == 0) { #ifdef HAVE_EXECINFO_H void *array[MAX_DEPTH]; size_t size; size = backtrace (array, MAX_DEPTH); backtrace_symbols_fd (array, size, 2); #else g_on_error_stack_trace (g_get_prgname ()); #endif /* HAVE_EXECINFO_H */ } else if (strcmp (ephy_debug_break, "trap") == 0) { /* FIXME: disable the handler for a moment so we * don't crash if we don't actually run under gdb */ G_BREAKPOINT (); } else if (strcmp (ephy_debug_break, "warn") == 0) { /* default behaviour only */ } else if (ephy_debug_break[0] != '\0') { g_print ("Unrecognised value of EPHY_DEBUG_BREAK env var: %s!\n", ephy_debug_break); } } } void ephy_debug_init (void) { #ifndef DISABLE_LOGGING ephy_log_modules = build_modules ("EPHY_LOG_MODULES", &ephy_log_all_modules); g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, log_module, NULL); #endif ephy_debug_break = g_getenv ("EPHY_DEBUG_BREAK"); g_log_set_default_handler (trap_handler, NULL); #ifndef DISABLE_PROFILING ephy_profile_modules = build_modules ("EPHY_PROFILE_MODULES", &ephy_profile_all_modules); #endif } #ifndef DISABLE_PROFILING static EphyProfiler * ephy_profiler_new (const char *name, const char *module) { EphyProfiler *profiler; profiler = g_new0 (EphyProfiler, 1); profiler->timer = g_timer_new (); profiler->name = g_strdup (name); profiler->module = g_strdup (module); g_timer_start (profiler->timer); return profiler; } static gboolean ephy_should_profile (const char *module) { char *slash; gboolean result = FALSE; guint i; slash = strrchr (module, '/'); /* Happens on builddir != srcdir builds */ if (slash != NULL) module = slash + 1; for (i = 0; ephy_profile_modules[i] != NULL; i++) { if (strcmp (ephy_profile_modules[i], module) == 0) { result = TRUE; break; } } return result; } static void ephy_profiler_dump (EphyProfiler *profiler) { double seconds; g_return_if_fail (profiler != NULL); seconds = g_timer_elapsed (profiler->timer, NULL); g_print ("[ %s ] %s %f s elapsed\n", profiler->module, profiler->name, seconds); } static void ephy_profiler_free (EphyProfiler *profiler) { g_return_if_fail (profiler != NULL); g_timer_destroy (profiler->timer); g_free (profiler->name); g_free (profiler->module); g_free (profiler); } void ephy_profiler_start (const char *name, const char *module) { EphyProfiler *profiler; if (ephy_profilers_hash == NULL) { ephy_profilers_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } if (!ephy_profile_all_modules && (ephy_profile_modules == NULL || !ephy_should_profile (module))) return; profiler = ephy_profiler_new (name, module); g_hash_table_insert (ephy_profilers_hash, g_strdup (name), profiler); } void ephy_profiler_stop (const char *name) { EphyProfiler *profiler; profiler = g_hash_table_lookup (ephy_profilers_hash, name); if (profiler == NULL) return; g_hash_table_remove (ephy_profilers_hash, name); ephy_profiler_dump (profiler); ephy_profiler_free (profiler); } #endif