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, int line_number, const gchar *function_name, const gchar *format, ...)
127 va_start(ap, format);
128 message = g_strdup_vprintf(format, ap);
131 if (options && options->log_window.timer_data)
133 location = g_strdup_printf("%s:%s:%d:%s:", get_exec_time(), file_name, line_number, function_name);
137 location = g_strdup_printf("%s:%d:%s:", file_name, line_number, function_name);
140 buf = g_strconcat(location, message, NULL);
141 log_domain_print_message(domain,buf);
146 void log_domain_printf(const gchar *domain, const gchar *format, ...)
151 va_start(ap, format);
152 buf = g_strdup_vprintf(format, ap);
155 log_domain_print_message(domain, buf);
159 * Debugging only functions
164 static gint debug_level = DEBUG_LEVEL_MIN;
167 gint get_debug_level()
172 void set_debug_level(gint new_level)
174 debug_level = CLAMP(new_level, DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX);
177 void debug_level_add(gint delta)
179 set_debug_level(debug_level + delta);
182 gint required_debug_level(gint level)
184 return (debug_level >= level);
187 static gint timeval_delta(struct timeval *result, struct timeval *x, struct timeval *y)
189 if (x->tv_usec < y->tv_usec)
191 gint nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
192 y->tv_usec -= 1000000 * nsec;
196 if (x->tv_usec - y->tv_usec > 1000000)
198 gint nsec = (x->tv_usec - y->tv_usec) / 1000000;
199 y->tv_usec += 1000000 * nsec;
203 result->tv_sec = x->tv_sec - y->tv_sec;
204 result->tv_usec = x->tv_usec - y->tv_usec;
206 return x->tv_sec < y->tv_sec;
209 const gchar *get_exec_time()
211 static gchar timestr[30];
212 static struct timeval start_tv = {0, 0};
213 static struct timeval previous = {0, 0};
214 static gint started = 0;
216 struct timeval tv = {0, 0};
217 static struct timeval delta = {0, 0};
219 gettimeofday(&tv, nullptr);
221 if (start_tv.tv_sec == 0) start_tv = tv;
223 tv.tv_sec -= start_tv.tv_sec;
224 if (tv.tv_usec >= start_tv.tv_usec)
225 tv.tv_usec -= start_tv.tv_usec;
228 tv.tv_usec += 1000000 - start_tv.tv_usec;
232 if (started) timeval_delta(&delta, &tv, &previous);
237 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));
242 void init_exec_time()
247 void set_regexp(const gchar *cmd_regexp)
249 regexp = g_strdup(cmd_regexp);
254 return g_strdup(regexp);
257 #ifdef HAVE_EXECINFO_H
259 * @brief Backtrace of geeqie files
264 * Requires command line program addr2line \n
265 * Prints the contents of the backtrace buffer for Geeqie files. \n
266 * Format printed is: \n
267 * <full path to source file>:<line number>
269 * The log window F1 command and Edit/Preferences/Behavior/Log Window F1
270 * Command may be used to open an editor at a backtrace location.
272 void log_print_backtrace(const gchar *file, gint line, const gchar *function)
277 gchar *address_offset;
280 gchar *function_name = nullptr;
287 if (runcmd(reinterpret_cast<const gchar *>("which addr2line >/dev/null 2>&1")) == 0)
289 exe_path = g_path_get_dirname(gq_executable_path);
290 bt_size = backtrace(bt, 1024);
291 bt_syms = backtrace_symbols(bt, bt_size);
293 log_printf("Backtrace start");
294 log_printf("%s/../%s:%d %s\n", exe_path, file, line, function);
296 /* Last item is always "??:?", so ignore it */
297 for (i = 1; i < bt_size - 1; i++)
299 if (strstr(bt_syms[i], GQ_APPNAME_LC))
301 paren_start = g_strstr_len(bt_syms[i], -1, "(");
302 paren_end = g_strstr_len(bt_syms[i], -1, ")");
303 address_offset = g_strndup(paren_start + 1, paren_end - paren_start - 1);
305 cmd_line = g_strconcat("addr2line -p -f -C -e ", gq_executable_path, " ", address_offset, NULL);
307 fp = popen(cmd_line, "r");
310 log_printf("Failed to run command: %s", cmd_line);
314 while (fgets(path, sizeof(path), fp) != nullptr)
316 /* Remove redundant newline */
317 path[strlen(path) - 1] = '\0';
319 if (g_strstr_len(path, strlen(path), "(") != nullptr)
321 function_name = g_strndup(path, g_strstr_len(path, strlen(path), "(") - path);
325 function_name = g_strdup("");
327 log_printf("%s %s", g_strstr_len(path, -1, "at ") + 3, function_name);
329 g_free(function_name);
335 g_free(address_offset);
339 log_printf("Backtrace end");
346 void log_print_backtrace(const gchar *, gint, const gchar *)
352 * @brief Print ref. count and image name
357 * Print image ref. count and full path name of all images in
358 * the file_data_pool.
360 void log_print_file_data_dump(const gchar *file, const gchar *function, gint line)
364 exe_path = g_path_get_dirname(gq_executable_path);
366 log_printf("FileData dump start");
367 log_printf("%s/../%s:%d %s\n", exe_path, file, line, function);
371 log_printf("FileData dump end");
377 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */