summaryrefslogtreecommitdiff
path: root/libnautilus-private
diff options
context:
space:
mode:
authorFederico Mena Quintero <federico@novell.com>2006-11-23 16:26:05 +0000
committerFederico Mena Quintero <federico@src.gnome.org>2006-11-23 16:26:05 +0000
commit3b4411675420822f4edf580e2f01423c8720123b (patch)
tree327f9f335fd0f4038ba8bf6a6d2144e92ba9bad1 /libnautilus-private
parent8ffdd1423d71f3691788190d201c98580500008d (diff)
downloadnautilus-3b4411675420822f4edf580e2f01423c8720123b.tar.gz
Use the write-to-a-pipe trick from the signal handler instead of queueing
2006-11-23 Federico Mena Quintero <federico@novell.com> Use the write-to-a-pipe trick from the signal handler instead of queueing an idle handler from there. * src/nautilus-main.c (setup_debug_log_signals): Set up a pair of pipes for the SIGUSR1 handler; set up a GIOChannel on them. (sigusr1_handler): Write a byte to our pipe instead of queueing an idle handler. (debug_log_io_cb): Replaces dump_debug_log_idle_cb(). Read from the pipe, and dump the debug log. Add a configuration file for the logging mechanism. You create ~/nautilus-debug-log.conf as a GKeyFile. * libnautilus-private/nautilus-debug-log.c (nautilus_debug_log_load_configuration): New function; loads the configuration from a key file like this: [debug log] enable domains = foo; bar; baz max lines = 1000 * libnautilus-private/nautilus-debug-log.c (dump_configuration): New utility function. (nautilus_debug_log_dump): At the end of the log, dump the configuration used for the debug log so that the user can re-create it later. * libnautilus-private/nautilus-debug-log.h: New prototype for nautilus_debug_log_load_configuration(). * src/nautilus-main.c (setup_debug_log): Load the debug log's configuration from ~/nautilus-debug-log.conf (setup_debug_log_domains): Removed. * src/nautilus-main.c (log_override_cb): If the log level of the message is G_LOG_LEVEL_DEBUG, don't log it as a milestone. We'll use this log level for miscellaneous debugging messages from gnome-vfs. Also, don't send G_LOG_LEVEL_DEBUG messages to the default log handler, to avoid a huge ~/.xsession-errors. (setup_debug_log_domains): Enable logging for NAUTILUS_DEBUG_LOG_DOMAIN_GLOG.
Diffstat (limited to 'libnautilus-private')
-rw-r--r--libnautilus-private/nautilus-debug-log.c637
-rw-r--r--libnautilus-private/nautilus-debug-log.h58
2 files changed, 695 insertions, 0 deletions
diff --git a/libnautilus-private/nautilus-debug-log.c b/libnautilus-private/nautilus-debug-log.c
new file mode 100644
index 000000000..5f7a9c53e
--- /dev/null
+++ b/libnautilus-private/nautilus-debug-log.c
@@ -0,0 +1,637 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ nautilus-debug-log.c: Ring buffer for logging debug messages
+
+ Copyright (C) 2006 Novell, Inc.
+
+ 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 of the
+ License, 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.
+
+ Author: Federico Mena-Quintero <federico@novell.com>
+*/
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <eel/eel-glib-extensions.h>
+#include "nautilus-debug-log.h"
+#include "nautilus-file.h"
+
+#define DEFAULT_RING_BUFFER_NUM_LINES 30000
+
+#define KEY_FILE_GROUP "debug log"
+#define KEY_FILE_DOMAINS_KEY "enable domains"
+#define KEY_FILE_MAX_LINES_KEY "max lines"
+
+static GStaticMutex log_mutex = G_STATIC_MUTEX_INIT;
+
+static GHashTable *domains_hash;
+static char **ring_buffer;
+static int ring_buffer_next_index;
+static int ring_buffer_num_lines;
+static int ring_buffer_max_lines = DEFAULT_RING_BUFFER_NUM_LINES;
+
+static void
+lock (void)
+{
+ g_static_mutex_lock (&log_mutex);
+}
+
+static void
+unlock (void)
+{
+ g_static_mutex_unlock (&log_mutex);
+}
+
+void
+nautilus_debug_log (gboolean is_milestone, const char *domain, const char *format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ nautilus_debug_logv (is_milestone, domain, NULL, format, args);
+ va_end (args);
+}
+
+static gboolean
+is_domain_enabled (const char *domain)
+{
+ /* User actions are always logged */
+ if (strcmp (domain, NAUTILUS_DEBUG_LOG_DOMAIN_USER) == 0)
+ return TRUE;
+
+ if (!domains_hash)
+ return FALSE;
+
+ return (g_hash_table_lookup (domains_hash, domain) != NULL);
+}
+
+static void
+ensure_ring (void)
+{
+ if (ring_buffer)
+ return;
+
+ ring_buffer = g_new0 (char *, ring_buffer_max_lines);
+ ring_buffer_next_index = 0;
+ ring_buffer_num_lines = 0;
+}
+
+static void
+add_to_ring (char *str)
+{
+ ensure_ring ();
+
+ g_assert (str != NULL);
+
+ if (ring_buffer_num_lines == ring_buffer_max_lines) {
+ /* We have an overlap, and the ring_buffer_next_index points to
+ * the "first" item. Free it to make room for the new item.
+ */
+
+ g_assert (ring_buffer[ring_buffer_next_index] != NULL);
+ g_free (ring_buffer[ring_buffer_next_index]);
+ } else
+ ring_buffer_num_lines++;
+
+ g_assert (ring_buffer_num_lines <= ring_buffer_max_lines);
+
+ ring_buffer[ring_buffer_next_index] = str;
+
+ ring_buffer_next_index++;
+ if (ring_buffer_next_index == ring_buffer_max_lines) {
+ ring_buffer_next_index = 0;
+ g_assert (ring_buffer_num_lines == ring_buffer_max_lines);
+ }
+}
+
+void
+nautilus_debug_logv (gboolean is_milestone, const char *domain, const GList *uris, const char *format, va_list args)
+{
+ char *str;
+ char *debug_str;
+ struct timeval tv;
+ struct tm tm;
+
+ lock ();
+
+ if (!(is_milestone || is_domain_enabled (domain)))
+ goto out;
+
+ str = g_strdup_vprintf (format, args);
+ gettimeofday (&tv, NULL);
+
+ tm = *localtime (&tv.tv_sec);
+
+ debug_str = g_strdup_printf ("%p %04d/%02d/%02d %02d:%02d:%02d.%04d (%s): %s",
+ g_thread_self (),
+ tm.tm_year + 1900,
+ tm.tm_mon + 1,
+ tm.tm_mday,
+ tm.tm_hour,
+ tm.tm_min,
+ tm.tm_sec,
+ (int) (tv.tv_usec / 100),
+ domain,
+ str);
+ g_free (str);
+
+ if (uris) {
+ int debug_str_len;
+ int uris_len;
+ const GList *l;
+ char *new_str;
+ char *p;
+
+ uris_len = 0;
+
+ for (l = uris; l; l = l->next) {
+ const char *uri;
+
+ uri = l->data;
+ uris_len += strlen (uri) + 2; /* plus 2 for a tab and the newline */
+ }
+
+ debug_str_len = strlen (debug_str);
+ new_str = g_new (char, debug_str_len + 1 + uris_len); /* plus 1 for newline */
+
+ p = g_stpcpy (new_str, debug_str);
+ *p++ = '\n';
+
+ for (l = uris; l; l = l->next) {
+ const char *uri;
+
+ uri = l->data;
+
+ *p++ = '\t';
+
+ p = g_stpcpy (p, uri);
+
+ if (l->next)
+ *p++ = '\n';
+ }
+
+ g_free (debug_str);
+ debug_str = new_str;
+ }
+
+ add_to_ring (debug_str);
+
+ /* FIXME: deal with milestones */
+
+ out:
+ unlock ();
+}
+
+void
+nautilus_debug_log_with_uri_list (gboolean is_milestone, const char *domain, const GList *uris,
+ const char *format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ nautilus_debug_logv (is_milestone, domain, uris, format, args);
+ va_end (args);
+}
+
+void
+nautilus_debug_log_with_file_list (gboolean is_milestone, const char *domain, GList *files,
+ const char *format, ...)
+{
+ va_list args;
+ GList *uris;
+ GList *l;
+
+ uris = NULL;
+
+ for (l = files; l; l = l->next) {
+ NautilusFile *file;
+ char *uri;
+
+ file = NAUTILUS_FILE (l->data);
+ uri = nautilus_file_get_uri (file);
+
+ if (nautilus_file_is_gone (file)) {
+ char *new_uri;
+
+ /* Hack: this will create an invalid URI, but it's for
+ * display purposes only.
+ */
+ new_uri = g_strconcat (uri ? uri : "", " (gone)", NULL);
+ g_free (uri);
+ uri = new_uri;
+ }
+ uris = g_list_prepend (uris, uri);
+ }
+
+ uris = g_list_reverse (uris);
+
+ va_start (args, format);
+ nautilus_debug_logv (is_milestone, domain, uris, format, args);
+ va_end (args);
+
+ eel_g_list_free_deep (uris);
+}
+
+gboolean
+nautilus_debug_log_load_configuration (const char *filename, GError **error)
+{
+ GKeyFile *key_file;
+ char **strings;
+ gsize num_strings;
+ int num;
+ GError *my_error;
+
+ g_assert (filename != NULL);
+ g_assert (error == NULL || *error == NULL);
+
+ key_file = g_key_file_new ();
+
+ if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error)) {
+ g_key_file_free (key_file);
+ return FALSE;
+ }
+
+ /* Domains */
+
+ my_error = NULL;
+ strings = g_key_file_get_string_list (key_file, KEY_FILE_GROUP, KEY_FILE_DOMAINS_KEY, &num_strings, &my_error);
+ if (my_error)
+ g_error_free (my_error);
+ else {
+ int i;
+
+ for (i = 0; i < num_strings; i++)
+ strings[i] = g_strstrip (strings[i]);
+
+ nautilus_debug_log_enable_domains ((const char **) strings, num_strings);
+ g_strfreev (strings);
+ }
+
+ /* Number of lines */
+
+ my_error = NULL;
+ num = g_key_file_get_integer (key_file, KEY_FILE_GROUP, KEY_FILE_MAX_LINES_KEY, &my_error);
+ if (my_error)
+ g_error_free (my_error);
+ else
+ nautilus_debug_log_set_max_lines (num);
+
+ g_key_file_free (key_file);
+ return TRUE;
+}
+
+void
+nautilus_debug_log_enable_domains (const char **domains, int n_domains)
+{
+ int i;
+
+ g_assert (domains != NULL);
+ g_assert (n_domains >= 0);
+
+ lock ();
+
+ if (!domains_hash)
+ domains_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ for (i = 0; i < n_domains; i++) {
+ g_assert (domains[i] != NULL);
+
+ if (strcmp (domains[i], NAUTILUS_DEBUG_LOG_DOMAIN_USER) == 0)
+ continue; /* user actions are always enabled */
+
+ if (g_hash_table_lookup (domains_hash, domains[i]) == NULL) {
+ char *domain;
+
+ domain = g_strdup (domains[i]);
+ g_hash_table_insert (domains_hash, domain, domain);
+ }
+ }
+
+ unlock ();
+}
+
+void
+nautilus_debug_log_disable_domains (const char **domains, int n_domains)
+{
+ int i;
+
+ g_assert (domains != NULL);
+ g_assert (n_domains >= 0);
+
+ lock ();
+
+ if (domains_hash) {
+ for (i = 0; i < n_domains; i++) {
+ char *domain;
+
+ g_assert (domains[i] != NULL);
+
+ if (strcmp (domains[i], NAUTILUS_DEBUG_LOG_DOMAIN_USER) == 0)
+ continue; /* user actions are always enabled */
+
+ domain = g_hash_table_lookup (domains_hash, domains[i]);
+ if (domain) {
+ g_hash_table_remove (domains_hash, domain);
+ g_free (domain);
+ }
+ }
+ } /* else, there is nothing to disable */
+
+ unlock ();
+}
+
+gboolean
+nautilus_debug_log_is_domain_enabled (const char *domain)
+{
+ gboolean retval;
+
+ g_assert (domain != NULL);
+
+ lock ();
+ retval = is_domain_enabled (domain);
+ unlock ();
+
+ return retval;
+}
+
+struct domains_dump_closure {
+ char **domains;
+ int num_domains;
+};
+
+static void
+domains_foreach_dump_cb (gpointer key, gpointer value, gpointer data)
+{
+ struct domains_dump_closure *closure;
+ char *domain;
+
+ closure = data;
+ domain = key;
+
+ closure->domains[closure->num_domains] = domain;
+ closure->num_domains++;
+}
+
+static GKeyFile *
+make_key_file_from_configuration (void)
+{
+ GKeyFile *key_file;
+ struct domains_dump_closure closure;
+ int num_domains;
+
+ key_file = g_key_file_new ();
+
+ /* domains */
+
+ if (domains_hash) {
+ num_domains = g_hash_table_size (domains_hash);
+
+ closure.domains = g_new (char *, num_domains);
+ closure.num_domains = 0;
+
+ g_hash_table_foreach (domains_hash, domains_foreach_dump_cb, &closure);
+ g_assert (num_domains == closure.num_domains);
+
+ g_key_file_set_string_list (key_file, KEY_FILE_GROUP, KEY_FILE_DOMAINS_KEY,
+ (const gchar * const *) closure.domains, closure.num_domains);
+ g_free (closure.domains);
+ }
+
+ /* max lines */
+
+ g_key_file_set_integer (key_file, KEY_FILE_GROUP, KEY_FILE_MAX_LINES_KEY, ring_buffer_max_lines);
+
+ return key_file;
+}
+
+static gboolean
+dump_configuration (const char *filename, FILE *file, GError **error)
+{
+ GKeyFile *key_file;
+ char *data;
+ gsize length;
+ gboolean success;
+
+ success = FALSE;
+
+ key_file = make_key_file_from_configuration ();
+
+ data = g_key_file_to_data (key_file, &length, error);
+ if (!data)
+ goto out;
+
+ if (fputs (data, file) == EOF) {
+ int saved_errno;
+
+ saved_errno = errno;
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (saved_errno),
+ "error when writing to log file %s", filename);
+
+ goto out;
+ }
+
+ success = TRUE;
+ out:
+ g_key_file_free (key_file);
+ return success;
+}
+
+gboolean
+nautilus_debug_log_dump (const char *filename, GError **error)
+{
+ FILE *file;
+ gboolean success;
+ int start_index;
+ int i;
+
+ g_assert (error == NULL || *error == NULL);
+
+ lock ();
+
+ success = FALSE;
+
+ file = fopen (filename, "w");
+ if (!file) {
+ int saved_errno;
+
+ saved_errno = errno;
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (saved_errno),
+ "could not open log file %s", filename);
+ goto out;
+ }
+
+ if (ring_buffer_num_lines == ring_buffer_max_lines)
+ start_index = ring_buffer_next_index;
+ else
+ start_index = 0;
+
+ for (i = 0; i < ring_buffer_num_lines; i++) {
+ int idx;
+
+ idx = (start_index + i) % ring_buffer_max_lines;
+
+ if (fputs (ring_buffer[idx], file) == EOF
+ || fputc ('\n', file) == EOF) {
+ int saved_errno;
+
+ saved_errno = errno;
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (saved_errno),
+ "error when writing to log file %s", filename);
+
+ goto do_close;
+ }
+ }
+
+ if (fputs ("\n\n"
+ "This configuration for the debug log can be re-created\n"
+ "by putting the following in ~/nautilus-debug-log.conf\n"
+ "(use ';' to separate domain names):\n\n",
+ file) == EOF) {
+ int saved_errno;
+
+ saved_errno = errno;
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (saved_errno),
+ "error when writing to log file %s", filename);
+
+ goto do_close;
+ }
+
+ if (!dump_configuration (filename, file, error))
+ goto do_close;
+
+ success = TRUE;
+
+ do_close:
+
+ if (fclose (file) != 0) {
+ int saved_errno;
+
+ saved_errno = errno;
+
+ if (error && *error) {
+ g_error_free (*error);
+ *error = NULL;
+ }
+
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (saved_errno),
+ "error when closing log file %s", filename);
+ success = FALSE;
+ }
+
+ out:
+
+ unlock ();
+ return success;
+}
+
+void
+nautilus_debug_log_set_max_lines (int num_lines)
+{
+ char **new_buffer;
+ int lines_to_copy;
+
+ g_assert (num_lines > 0);
+
+ lock ();
+
+ if (num_lines == ring_buffer_max_lines)
+ goto out;
+
+ new_buffer = g_new0 (char *, num_lines);
+
+ lines_to_copy = MIN (num_lines, ring_buffer_num_lines);
+
+ if (ring_buffer) {
+ int start_index;
+ int i;
+
+ if (ring_buffer_num_lines == ring_buffer_max_lines)
+ start_index = (ring_buffer_next_index + ring_buffer_max_lines - lines_to_copy) % ring_buffer_max_lines;
+ else
+ start_index = ring_buffer_num_lines - lines_to_copy;
+
+ g_assert (start_index >= 0 && start_index < ring_buffer_max_lines);
+
+ for (i = 0; i < lines_to_copy; i++) {
+ int idx;
+
+ idx = (start_index + i) % ring_buffer_max_lines;
+
+ new_buffer[i] = ring_buffer[idx];
+ ring_buffer[idx] = NULL;
+ }
+
+ for (i = 0; i < ring_buffer_max_lines; i++)
+ g_free (ring_buffer[i]);
+
+ g_free (ring_buffer);
+ }
+
+ ring_buffer = new_buffer;
+ ring_buffer_next_index = lines_to_copy;
+ ring_buffer_num_lines = lines_to_copy;
+ ring_buffer_max_lines = num_lines;
+
+ out:
+
+ unlock ();
+}
+
+int
+nautilus_debug_log_get_max_lines (void)
+{
+ int retval;
+
+ lock ();
+ retval = ring_buffer_max_lines;
+ unlock ();
+
+ return retval;
+}
+
+void
+nautilus_debug_log_clear (void)
+{
+ int i;
+
+ lock ();
+
+ if (!ring_buffer)
+ goto out;
+
+ for (i = 0; i < ring_buffer_max_lines; i++) {
+ g_free (ring_buffer[i]);
+ ring_buffer[i] = NULL;
+ }
+
+ ring_buffer_next_index = 0;
+ ring_buffer_num_lines = 0;
+
+ out:
+ unlock ();
+}
diff --git a/libnautilus-private/nautilus-debug-log.h b/libnautilus-private/nautilus-debug-log.h
new file mode 100644
index 000000000..801610d50
--- /dev/null
+++ b/libnautilus-private/nautilus-debug-log.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ nautilus-debug-log.h: Ring buffer for logging debug messages
+
+ Copyright (C) 2006 Novell, Inc.
+
+ 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 of the
+ License, 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.
+
+ Author: Federico Mena-Quintero <federico@novell.com>
+*/
+
+#ifndef NAUTILUS_DEBUG_LOG_H
+#define NAUTILUS_DEBUG_LOG_H
+
+#include <glib.h>
+
+#define NAUTILUS_DEBUG_LOG_DOMAIN_USER "USER" /* always enabled */
+#define NAUTILUS_DEBUG_LOG_DOMAIN_ASYNC "async" /* when asynchronous notifications come in */
+#define NAUTILUS_DEBUG_LOG_DOMAIN_GLOG "GLog" /* used for GLog messages; don't use it yourself */
+
+void nautilus_debug_log (gboolean is_milestone, const char *domain, const char *format, ...);
+
+void nautilus_debug_log_with_uri_list (gboolean is_milestone, const char *domain, const GList *uris,
+ const char *format, ...);
+void nautilus_debug_log_with_file_list (gboolean is_milestone, const char *domain, GList *files,
+ const char *format, ...);
+
+void nautilus_debug_logv (gboolean is_milestone, const char *domain, const GList *uris, const char *format, va_list args);
+
+gboolean nautilus_debug_log_load_configuration (const char *filename, GError **error);
+
+void nautilus_debug_log_enable_domains (const char **domains, int n_domains);
+void nautilus_debug_log_disable_domains (const char **domains, int n_domains);
+
+gboolean nautilus_debug_log_is_domain_enabled (const char *domain);
+
+gboolean nautilus_debug_log_dump (const char *filename, GError **error);
+
+void nautilus_debug_log_set_max_lines (int num_lines);
+int nautilus_debug_log_get_max_lines (void);
+
+/* For testing only */
+void nautilus_debug_log_clear (void);
+
+#endif /* NAUTILUS_DEBUG_LOG_H */