From: Colin Clark Date: Mon, 20 Feb 2023 14:06:21 +0000 (+0000) Subject: Include DEBUG_BT() and DEBUG_FD() functions X-Git-Tag: v2.1~106 X-Git-Url: http://geeqie.org/cgi-bin/gitweb.cgi?p=geeqie.git;a=commitdiff_plain;h=cd72fa8cf4c85a1af8f66320d48dbee00ad16ec5 Include DEBUG_BT() and DEBUG_FD() functions - DEBUG_BT print backtrace of Geeqie calls - DEBUG_FD print dump of FileData hash file --- diff --git a/CODING.md b/CODING.md index 971b6a9b..cd831e97 100644 --- 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: +log_window.action> +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 diff --git a/config.h.in b/config.h.in index 24f8ee0c..e356592c 100644 --- a/config.h.in +++ b/config.h.in @@ -218,4 +218,6 @@ /* Define for large files, on AIX-style hosts. */ /* #undef _LARGE_FILES */ +/* Define to 1 if you have the header file. */ +#mesondefine HAVE_EXECINFO_H #endif diff --git a/doxygen.conf b/doxygen.conf index 5ee4ef13..7676c174 100644 --- a/doxygen.conf +++ b/doxygen.conf @@ -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 \ diff --git a/meson.build b/meson.build index 7d7efd88..653dc08c 100644 --- a/meson.build +++ b/meson.build @@ -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) diff --git a/src/debug.cc b/src/debug.cc index 3ec56785..940c3521 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -21,9 +21,15 @@ #include "main.h" #include "debug.h" +#include "filedata.h" #include "logwindow.h" +#include "misc.h" #include "ui-fileops.h" +#ifdef HAVE_EXECINFO_H +#include +#endif + #include /* @@ -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 + * : + * + * 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: */ diff --git a/src/debug.h b/src/debug.h index 06b7b8d4..02883a13 100644 --- a/src/debug.h +++ b/src/debug.h @@ -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 */ diff --git a/src/filedata.cc b/src/filedata.cc index 5c165b7f..daaabf73 100644 --- a/src/filedata.cc +++ b/src/filedata.cc @@ -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); diff --git a/src/filedata.h b/src/filedata.h index d5160e0e..49a1476f 100644 --- a/src/filedata.h +++ b/src/filedata.h @@ -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: */ diff --git a/src/logwindow.cc b/src/logwindow.cc index 183380ee..c9069ded 100644 --- a/src/logwindow.cc +++ b/src/logwindow.cc @@ -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 + * log_window.action> + * +*/ +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; } diff --git a/src/options.cc b/src/options.cc index 24f1efc9..9680030d 100644 --- a/src/options.cc +++ b/src/options.cc @@ -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; diff --git a/src/options.h b/src/options.h index a4dae1d6..60003efe 100644 --- a/src/options.h +++ b/src/options.h @@ -348,6 +348,7 @@ struct _ConfOptions gboolean paused; gboolean line_wrap; gboolean timer_data; + gchar *action; /** Used with F1 key */ } log_window; /* star rating */ diff --git a/src/rcfile.cc b/src/rcfile.cc index 1d8bf878..032eb9a6 100644 --- a/src/rcfile.cc +++ b/src/rcfile.cc @@ -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;