/* * Copyright © 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "config.h" #include "ephy-debug.h" #include #ifdef HAVE_EXECINFO_H #include #endif #include #include #include #include /** * SECTION:ephy-debug * @short_description: Epiphany debugging and profiling facilities * * Epiphany includes powerful profiling and debugging facilities to log and * analyze modules. Refer to doc/debugging.txt for more information. */ 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_modules != NULL) { guint i; for (i = 0; ephy_log_modules[i] != NULL; i++) { if (strstr (message, ephy_log_modules [i]) != 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); } } } /** * ephy_debug_init: * * Starts the debugging facility, see doc/debugging.txt in Epiphany's source for * more information. It also starts module logging and profiling if the * appropiate variables are set: EPHY_LOG_MODULES and EPHY_PROFILE_MODULES. **/ 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); } /** * ephy_profiler_start: * @name: name of this new profiler * @module: Epiphany module to profile * * Starts a new profiler on @module naming it @name. **/ 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); } /** * ephy_profiler_stop: * @name: name of the profiler to stop * * Stops the profiler named @name. **/ 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