Include DEBUG_BT() and DEBUG_FD() functions
authorColin Clark <colin.clark@cclark.uk>
Mon, 20 Feb 2023 14:06:21 +0000 (14:06 +0000)
committerColin Clark <colin.clark@cclark.uk>
Mon, 20 Feb 2023 14:06:21 +0000 (14:06 +0000)
- DEBUG_BT print backtrace of Geeqie calls
- DEBUG_FD print dump of FileData hash file

12 files changed:
CODING.md
config.h.in
doxygen.conf
meson.build
src/debug.cc
src/debug.h
src/filedata.cc
src/filedata.h
src/logwindow.cc
src/options.cc
src/options.h
src/rcfile.cc

index 971b6a9..cd831e9 100644 (file)
--- a/CODING.md
+++ b/CODING.md
@@ -42,6 +42,24 @@ For use with the [GTKInspector](https://wiki.gnome.org/action/show/Projects/GTK/
 Sample command line call:  
 `GTK_DEBUG=interactive src/geeqie`
 
+### DEBUG_BT()
+
+Prints a backtrace.
+Use only for temporary debugging i.e. not in code in the repository
+
+```text
+When the LogWindow has focus, the F1 key executes the following action as a command line program:
+<options->log_window.action> <selected text>
+The log_window.action value must be set by editing the geeqierc.xml file manually.
+If no text is selected when the F1 key is pressed, the text either side of the cursor delimited by a space character or the beginning or end of the line is selected.
+This feature may be used to open an editor at a file location listed in the backtrace.
+```
+
+### DEBUG_FD()
+
+Prints a dump of the FileData hash list as a ref. count followed by the full path of the item.
+Use only for temporary debugging i.e. not in code in the repository
+
 ---
 
 ## GPL header
index 24f8ee0..e356592 100644 (file)
 /* Define for large files, on AIX-style hosts. */
 /* #undef _LARGE_FILES */
 
+/* Define to 1 if you have the <execinfo.h> header file. */
+#mesondefine HAVE_EXECINFO_H
 #endif
index 5ee4ef1..7676c17 100644 (file)
@@ -2260,14 +2260,17 @@ INCLUDE_FILE_PATTERNS  =
 # recursively expanded use the := operator instead of the = operator.
 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-PREDEFINED             = HAVE_ARCHIVE=1 \
+PREDEFINED             = DEBUG=1 \
+                         HAVE_ARCHIVE=1 \
                          HAVE_CLUTTER=1 \
                          HAVE_DJVU=1 \
+                         HAVE_EXECINFO_H=1 \
                          HAVE_EXIV2=1 \
                          HAVE_FFMPEGTHUMBNAILER=1 \
                          HAVE_HEIF=1 \
                          HAVE_J2K=1 \
                          HAVE_JPEG=1 \
+                         HAVE_JPEGXL=1 \
                          HAVE_LCMS=1 \
                          HAVE_LCMS2=1 \
                          HAVE_LIBCHAMPLAIN=1 \
index 7d7efd8..653dc08 100644 (file)
@@ -500,6 +500,12 @@ else
     summary({'nl_langinfo' : ['nl_langinfo not found - first weekday depends on locale:', false, 'first weekday defaults to Monday']}, section : 'Documentation', bool_yn : true)
 endif
 
+# Required only for backtrace debugging
+result = cc.check_header('execinfo.h')
+if result
+    conf_data.set('HAVE_EXECINFO_H', 1)
+endif
+
 conf_data.set_quoted('GETTEXT_PACKAGE', meson.project_name())
 conf_data.set_quoted('GQ_APPDIR', gq_appdir)
 conf_data.set_quoted('GQ_BINDIR', gq_bindir)
index 3ec5678..940c352 100644 (file)
 #include "main.h"
 #include "debug.h"
 
+#include "filedata.h"
 #include "logwindow.h"
+#include "misc.h"
 #include "ui-fileops.h"
 
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
 #include <regex.h>
 
 /*
@@ -250,5 +256,118 @@ gchar *get_regexp(void)
        return g_strdup(regexp);
 }
 
+#ifdef HAVE_EXECINFO_H
+/**
+ * @brief Backtrace of geeqie files
+ * @param file 
+ * @param function 
+ * @param line 
+ * 
+ * Requires command line program addr2line \n
+ * Prints the contents of the backtrace buffer for Geeqie files. \n
+ * Format printed is: \n
+ * <full path to source file>:<line number>
+ *
+ * The log window F1 command and options->log_window.action may be used
+ * to open an editor at a backtrace location.
+ */
+void log_print_backtrace(const gchar *file, const gchar *function, gint line)
+{
+       FILE *fp;
+       char **bt_syms;
+       char path[2048];
+       gchar *address_offset;
+       gchar *cmd_line;
+       gchar *exe_path;
+       gchar *function_name;
+       gchar *paren_end;
+       gchar *paren_start;
+       gint bt_size;
+       gint i;
+       void *bt[1024];
+
+       if (runcmd((gchar *)"which addr2line >/dev/null 2>&1") == 0)
+               {
+               exe_path = g_path_get_dirname(gq_executable_path);
+               bt_size = backtrace(bt, 1024);
+               bt_syms = backtrace_symbols(bt, bt_size);
+
+               log_printf("Backtrace start");
+               log_printf("%s/../%s:%d %s\n", exe_path, file, line, function);
+
+               /* Last item is always "??:?", so ignore it */
+               for (i = 1; i < bt_size - 1; i++)
+                       {
+                       if (strstr(bt_syms[i], GQ_APPNAME_LC))
+                               {
+                               paren_start = g_strstr_len(bt_syms[i], -1, "(");
+                               paren_end = g_strstr_len(bt_syms[i], -1, ")");
+                               address_offset = g_strndup(paren_start + 1, paren_end - paren_start - 1);
+
+                               cmd_line = g_strconcat("addr2line -p -f -C -e ", gq_executable_path, " ", address_offset, NULL);
+
+                               fp = popen(cmd_line, "r");
+                               if (fp == NULL)
+                                       {
+                                       log_printf("Failed to run command: %s", cmd_line);
+                                       }
+                               else
+                                       {
+                                       while (fgets(path, sizeof(path), fp) != NULL)
+                                               {
+                                               /* Remove redundant newline */
+                                               path[strlen(path) - 1] = '\0';
+
+                                               function_name = g_strndup(path, g_strstr_len(path, strlen(path), "(") - path);
+
+                                               log_printf("%s %s", g_strstr_len(path, -1, "at ") + 3, function_name);
+
+                                               g_free(function_name);
+                                               }
+                                       }
+
+                               pclose(fp);
+
+                               g_free(address_offset);
+                               g_free(cmd_line);
+                               }
+                       }
+               log_printf("Backtrace end");
+
+               free(bt_syms);
+               g_free(exe_path);
+               }
+}
+#else
+void log_print_backtrace(const gchar *file, const gchar *function, gint line)
+{
+}
+#endif
+
+/**
+ * @brief Print ref. count and image name
+ * @param file 
+ * @param function 
+ * @param line 
+ * 
+ * Print image ref. count and full path name of all images in
+ * the file_data_pool.
+ */
+void log_print_file_data_dump(const gchar *file, const gchar *function, gint line)
+{
+       gchar *exe_path;
+
+       exe_path = g_path_get_dirname(gq_executable_path);
+
+       log_printf("FileData dump start");
+       log_printf("%s/../%s:%d %s\n", exe_path, file, line, function);
+
+       file_data_dump();
+
+       log_printf("FileData dump end");
+
+       g_free(exe_path);
+}
+
 #endif /* DEBUG */
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
index 06b7b8d..02883a1 100644 (file)
@@ -29,6 +29,9 @@
 void log_domain_printf(const gchar *domain, const gchar *format, ...) G_GNUC_PRINTF(2, 3);
 void log_domain_print_debug(const gchar *domain, const gchar *file_name, const gchar *function_name,
                                                        int line_number, const gchar *format, ...) G_GNUC_PRINTF(5, 6);
+void log_print_file_data_dump(const gchar *file, const gchar *function_name, gint line_number);
+void log_print_backtrace(const gchar *file, const gchar *function_name, gint line_number);
+
 #define log_printf(...) log_domain_printf(DOMAIN_INFO, __VA_ARGS__)
 
 #ifdef DEBUG
@@ -73,6 +76,15 @@ void init_exec_time(void);
                                gtk_widget_set_name(GTK_WIDGET(widget), g_strdup_printf("%s:%d", __FILE__, __LINE__)); \
                                } while(0)
 
+#define DEBUG_BT() do \
+                               { \
+                               log_print_backtrace(__FILE__, __func__, __LINE__); \
+                               } while(0)
+
+#define DEBUG_FD() do \
+                               { \
+                               log_print_file_data_dump(__FILE__, __func__, __LINE__); \
+                               } while(0)
 #else /* DEBUG */
 
 #define get_regexp() (0)
@@ -87,6 +99,8 @@ void init_exec_time(void);
 #define DEBUG_N(n, ...)  do { } while(0)
 
 #define DEBUG_NAME(widget) do { } while(0)
+#define DEBUG_BT() do { } while(0)
+#define DEBUG_FD() do { } while(0)
 
 #endif /* DEBUG */
 
index 5c165b7..daaabf7 100644 (file)
@@ -686,6 +686,38 @@ FileData *file_data_ref(FileData *fd)
        return fd;
 }
 
+/**
+ * @brief Print ref. count and image name
+ * @param  
+ * 
+ * Print image ref. count and full path name of all images in
+ * the file_data_pool.
+ *
+ * Used only by DEBUG_FD()
+ */
+void file_data_dump()
+{
+       FileData *fd;
+       GList *list;
+
+       if (file_data_pool)
+               {
+               list = g_hash_table_get_values(file_data_pool);
+
+               log_printf("%d", global_file_data_count);
+               log_printf("%d", g_list_length(list));
+
+               while (list)
+                       {
+                       fd = list->data;
+                       log_printf("%-4d %s", fd->ref, fd->path);
+                       list = list->next;
+                       }
+
+               g_list_free(list);
+               }
+}
+
 static void file_data_free(FileData *fd)
 {
        g_assert(fd->magick == FD_MAGICK);
index d5160e0..49a1476 100644 (file)
@@ -188,5 +188,7 @@ void file_data_inc_page_num(FileData *fd);
 void file_data_dec_page_num(FileData *fd);
 void file_data_set_page_total(FileData *fd, gint page_total);
 void file_data_set_page_num(FileData *fd, gint page_num);
+
+void file_data_dump();
 #endif
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
index 183380e..c9069de 100644 (file)
@@ -56,11 +56,90 @@ static void hide_cb(GtkWidget *UNUSED(widget), LogWindow *UNUSED(logwin))
 {
 }
 
-static gboolean key_pressed(GtkWidget *UNUSED(widget), GdkEventKey *event,
-                           LogWindow *logwin)
+static gboolean iter_char_search_cb(gunichar ch, gpointer data)
 {
+       gboolean ret;
+
+       if (ch == GPOINTER_TO_UINT(data))
+               {
+               ret = TRUE;
+               }
+       else
+               {
+               ret = FALSE;
+               }
+
+       return ret;
+}
+
+/**
+ * @brief Handle escape and F1 keys
+ * @param UNUSED 
+ * @param event 
+ * @param logwin 
+ * @returns 
+ * 
+ * If escape key pressed, hide log window. \n
+ * If no text selected, form a selection bounded by space characters or
+ * start and end of line. \n
+ * If F1 pressed, execute command line program: \n
+ * <options->log_window.action> <selected text>
+ * 
+*/
+static gboolean key_pressed(GtkWidget *UNUSED(widget), GdkEventKey *event, LogWindow *logwin)
+{
+       GtkTextBuffer *buffer;
+       GtkTextIter chr_end;
+       GtkTextIter chr_start;
+       GtkTextIter cursor_iter;
+       GtkTextIter line_end;
+       GtkTextIter line_start;
+       GtkTextMark *cursor_mark;
+       gchar *cmd_line;
+       gchar *sel_text;
+
        if (event && event->keyval == GDK_KEY_Escape)
                gtk_widget_hide(logwin->window);
+
+       if (event && event->keyval == GDK_KEY_F1)
+               {
+               if (strlen(options->log_window.action) > 0)
+                       {
+                       buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(logwin->text));
+
+                       if (!gtk_text_buffer_get_has_selection(buffer))
+                               {
+                               cursor_mark = gtk_text_buffer_get_insert(buffer);
+                               gtk_text_buffer_get_iter_at_mark(buffer, &cursor_iter, cursor_mark);
+
+                               line_start = cursor_iter;
+                               gtk_text_iter_set_line_offset(&line_start, 0);
+                               line_end = cursor_iter;
+                               gtk_text_iter_forward_to_line_end(&line_end);
+
+                               chr_start = cursor_iter;
+                               gtk_text_iter_backward_find_char(&chr_start, (GtkTextCharPredicate)iter_char_search_cb, GUINT_TO_POINTER(' '), &line_start);
+
+                               chr_end = cursor_iter;
+                               gtk_text_iter_forward_find_char(&chr_end, (GtkTextCharPredicate)iter_char_search_cb, GUINT_TO_POINTER(' '), &line_end);
+
+                               gtk_text_buffer_select_range(buffer, &chr_start, &chr_end);
+                               }
+
+                       if (gtk_text_buffer_get_selection_bounds(gtk_text_view_get_buffer(GTK_TEXT_VIEW(logwin->text)), &chr_start, &chr_end))
+                               {
+                               sel_text = gtk_text_buffer_get_text( gtk_text_view_get_buffer(GTK_TEXT_VIEW(logwin->text)), &chr_start, &chr_end, FALSE);
+
+                               cmd_line = g_strconcat(options->log_window.action, " ", sel_text, NULL);
+
+                               runcmd(cmd_line);
+
+                               g_free(sel_text);
+                               g_free(cmd_line);
+                               }
+                       }
+               }
+
        return FALSE;
 }
 
index 24f1efc..9680030 100644 (file)
@@ -203,6 +203,7 @@ ConfOptions *init_options(ConfOptions *options)
        options->log_window.line_wrap = FALSE;
        options->log_window.paused = FALSE;
        options->log_window.timer_data = FALSE;
+       options->log_window.action = g_strdup("");
 
        options->read_metadata_in_idle = FALSE;
        options->star_rating.star = STAR_RATING_STAR;
index a4dae1d..60003ef 100644 (file)
@@ -348,6 +348,7 @@ struct _ConfOptions
                gboolean paused;
                gboolean line_wrap;
                gboolean timer_data;
+               gchar *action; /** Used with F1 key */
        } log_window;
 
        /* star rating */
index 1d8bf87..032eb9a 100644 (file)
@@ -346,6 +346,7 @@ static void write_global_attributes(GString *outstr, gint indent)
 
        WRITE_NL(); WRITE_UINT(*options, log_window_lines);
        WRITE_NL(); WRITE_BOOL(*options, log_window.timer_data);
+       WRITE_NL(); WRITE_CHAR(*options, log_window.action);
 
        WRITE_NL(); WRITE_BOOL(*options, appimage_notifications);
        WRITE_NL(); WRITE_BOOL(*options, marks_save);
@@ -847,6 +848,7 @@ static gboolean load_global_params(const gchar **attribute_names, const gchar **
 
                if (READ_INT(*options, log_window_lines)) continue;
                if (READ_BOOL(*options, log_window.timer_data)) continue;
+               if (READ_CHAR(*options, log_window.action)) continue;
 
                if (READ_BOOL(*options, appimage_notifications)) continue;
                if (READ_BOOL(*options, marks_save)) continue;