2 * Copyright (C) 2008 - 2016 The Geeqie Team
4 * Authors: Vladimir Nadvornik, Laurent Monin
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "logwindow.h"
27 #include "ui-fileops.h"
29 #ifdef HAVE_EXECINFO_H
38 static gchar *regexp = nullptr;
40 static gboolean log_msg_cb(gpointer data)
42 auto buf = static_cast<gchar *>(data);
43 log_window_append(buf, LOG_MSG);
49 * @brief Appends a user information message to the log window queue
50 * @param data The message
53 * If the first word of the message is either "error" or "warning"
54 * (case insensitive) the message is color-coded appropriately
56 static gboolean log_normal_cb(gpointer data)
58 auto buf = static_cast<gchar *>(data);
59 gchar *buf_casefold = g_utf8_casefold(buf, -1);
60 gchar *error_casefold = g_utf8_casefold(_("error"), -1);
61 gchar *warning_casefold = g_utf8_casefold(_("warning"), -1);
63 if (buf_casefold == g_strstr_len(buf_casefold, -1, error_casefold))
65 log_window_append(buf, LOG_ERROR);
67 else if (buf_casefold == g_strstr_len(buf_casefold, -1, warning_casefold))
69 log_window_append(buf, LOG_WARN);
73 log_window_append(buf, LOG_NORMAL);
78 g_free(error_casefold);
79 g_free(warning_casefold);
83 void log_domain_print_message(const gchar *domain, gchar *buf)
87 gint ret_comp, ret_exec;
89 buf_nl = g_strconcat(buf, "\n", NULL);
91 if (regexp && command_line)
93 ret_comp = regcomp(®ex, regexp, 0);
96 ret_exec = regexec(®ex, buf_nl, 0, nullptr, 0);
100 print_term(FALSE, buf_nl);
101 if (strcmp(domain, DOMAIN_INFO) == 0)
102 g_idle_add(log_normal_cb, buf_nl);
104 g_idle_add(log_msg_cb, buf_nl);
111 print_term(FALSE, buf_nl);
112 if (strcmp(domain, DOMAIN_INFO) == 0)
113 g_idle_add(log_normal_cb, buf_nl);
115 g_idle_add(log_msg_cb, buf_nl);
120 void log_domain_print_debug(const gchar *domain, const gchar *file_name, const gchar *function_name,
121 int line_number, const gchar *format, ...)
128 va_start(ap, format);
129 message = g_strdup_vprintf(format, ap);
132 if (options && options->log_window.timer_data)
134 location = g_strdup_printf("%s:%s:%s:%d:", get_exec_time(), file_name,
135 function_name, line_number);
139 location = g_strdup_printf("%s:%s:%d:", file_name, function_name, line_number);
142 buf = g_strconcat(location, message, NULL);
143 log_domain_print_message(domain,buf);
148 void log_domain_printf(const gchar *domain, const gchar *format, ...)
153 va_start(ap, format);
154 buf = g_strdup_vprintf(format, ap);
157 log_domain_print_message(domain, buf);
161 * Debugging only functions
166 static gint debug_level = DEBUG_LEVEL_MIN;
169 gint get_debug_level()
174 void set_debug_level(gint new_level)
176 debug_level = CLAMP(new_level, DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX);
179 void debug_level_add(gint delta)
181 set_debug_level(debug_level + delta);
184 gint required_debug_level(gint level)
186 return (debug_level >= level);
189 static gint timeval_delta(struct timeval *result, struct timeval *x, struct timeval *y)
191 if (x->tv_usec < y->tv_usec)
193 gint nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
194 y->tv_usec -= 1000000 * nsec;
198 if (x->tv_usec - y->tv_usec > 1000000)
200 gint nsec = (x->tv_usec - y->tv_usec) / 1000000;
201 y->tv_usec += 1000000 * nsec;
205 result->tv_sec = x->tv_sec - y->tv_sec;
206 result->tv_usec = x->tv_usec - y->tv_usec;
208 return x->tv_sec < y->tv_sec;
211 const gchar *get_exec_time()
213 static gchar timestr[30];
214 static struct timeval start_tv = {0, 0};
215 static struct timeval previous = {0, 0};
216 static gint started = 0;
218 struct timeval tv = {0, 0};
219 static struct timeval delta = {0, 0};
221 gettimeofday(&tv, nullptr);
223 if (start_tv.tv_sec == 0) start_tv = tv;
225 tv.tv_sec -= start_tv.tv_sec;
226 if (tv.tv_usec >= start_tv.tv_usec)
227 tv.tv_usec -= start_tv.tv_usec;
230 tv.tv_usec += 1000000 - start_tv.tv_usec;
234 if (started) timeval_delta(&delta, &tv, &previous);
239 g_snprintf(timestr, sizeof(timestr), "%5d.%06d (+%05d.%06d)", static_cast<gint>(tv.tv_sec), static_cast<gint>(tv.tv_usec), static_cast<gint>(delta.tv_sec), static_cast<gint>(delta.tv_usec));
244 void init_exec_time()
249 void set_regexp(const gchar *cmd_regexp)
251 regexp = g_strdup(cmd_regexp);
256 return g_strdup(regexp);
259 #ifdef HAVE_EXECINFO_H
261 * @brief Backtrace of geeqie files
266 * Requires command line program addr2line \n
267 * Prints the contents of the backtrace buffer for Geeqie files. \n
268 * Format printed is: \n
269 * <full path to source file>:<line number>
271 * The log window F1 command and options->log_window.action may be used
272 * to open an editor at a backtrace location.
274 void log_print_backtrace(const gchar *file, const gchar *function, gint line)
279 gchar *address_offset;
282 gchar *function_name = nullptr;
289 if (runcmd(reinterpret_cast<const gchar *>("which addr2line >/dev/null 2>&1")) == 0)
291 exe_path = g_path_get_dirname(gq_executable_path);
292 bt_size = backtrace(bt, 1024);
293 bt_syms = backtrace_symbols(bt, bt_size);
295 log_printf("Backtrace start");
296 log_printf("%s/../%s:%d %s\n", exe_path, file, line, function);
298 /* Last item is always "??:?", so ignore it */
299 for (i = 1; i < bt_size - 1; i++)
301 if (strstr(bt_syms[i], GQ_APPNAME_LC))
303 paren_start = g_strstr_len(bt_syms[i], -1, "(");
304 paren_end = g_strstr_len(bt_syms[i], -1, ")");
305 address_offset = g_strndup(paren_start + 1, paren_end - paren_start - 1);
307 cmd_line = g_strconcat("addr2line -p -f -C -e ", gq_executable_path, " ", address_offset, NULL);
309 fp = popen(cmd_line, "r");
312 log_printf("Failed to run command: %s", cmd_line);
316 while (fgets(path, sizeof(path), fp) != nullptr)
318 /* Remove redundant newline */
319 path[strlen(path) - 1] = '\0';
321 if (g_strstr_len(path, strlen(path), "(") != nullptr)
323 function_name = g_strndup(path, g_strstr_len(path, strlen(path), "(") - path);
327 function_name = g_strdup("");
329 log_printf("%s %s", g_strstr_len(path, -1, "at ") + 3, function_name);
331 g_free(function_name);
337 g_free(address_offset);
341 log_printf("Backtrace end");
348 void log_print_backtrace(const gchar *, const gchar *, gint)
354 * @brief Print ref. count and image name
359 * Print image ref. count and full path name of all images in
360 * the file_data_pool.
362 void log_print_file_data_dump(const gchar *file, const gchar *function, gint line)
366 exe_path = g_path_get_dirname(gq_executable_path);
368 log_printf("FileData dump start");
369 log_printf("%s/../%s:%d %s\n", exe_path, file, line, function);
373 log_printf("FileData dump end");
379 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */