Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
system_signal_handling.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2016-2018 Peter Budai.
4 Copyright (C) 2016 Roman Lebedev.
5 Copyright (C) 2017 luzpaz.
6 Copyright (C) 2017 Tobias Ellinghaus.
7 Copyright (C) 2020 Pascal Obry.
8 Copyright (C) 2022 Aurélien PIERRE.
9 Copyright (C) 2022 Martin Bařinka.
10
11 darktable is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
15
16 darktable is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with darktable. If not, see <http://www.gnu.org/licenses/>.
23*/
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include "common/darktable.h" // for darktable, darktable_t
30#include "common/sentry.h" // for dt_sentry_backtrace_captured
32#include <errno.h> // for errno
33#include <fcntl.h> // for O_APPEND, O_CREAT, O_WRONLY, open
34#include <glib.h> // for g_free, g_printerr, g_strdup_printf
35#include <glib/gstdio.h> // for g_unlink
36#include <signal.h> // for signal, SIGSEGV, SIG_ERR
37#include <stddef.h> // for NULL
38#include <stdio.h> // for dprintf, fprintf, stderr
39#include <string.h> // for strerror
40#include <unistd.h> // for STDOUT_FILENO, close, execlp, fork
41
42#ifdef __linux__
43#include <sys/prctl.h> // for PR_SET_PTRACER, prctl
44#endif
45
46#ifndef _WIN32
47#include <sys/wait.h> // for waitpid
48#endif
49
50#ifdef _WIN32
51#include <exchndl.h>
52#endif //_WIN32
53
54#if defined(__linux__) && !defined(PR_SET_PTRACER)
55#define PR_SET_PTRACER 0x59616d61
56#endif
57
59
60#if !defined(__APPLE__) && !defined(_WIN32)
62#endif
63
64// deer graphicsmagick, please stop messing with the stuff that you should not be touching at all.
65// based on GM's InitializeMagickSignalHandlers() and MagickSignalHandlerMessage()
66#if !defined(_WIN32)
67static const int _signals_to_preserve[] = { SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGBUS, SIGFPE,
68 SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGXCPU, SIGXFSZ };
69#else
70static const int _signals_to_preserve[] = { SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM };
71static LPTOP_LEVEL_EXCEPTION_FILTER _dt_exceptionfilter_old_handler = NULL;
72#endif
73
74#define _NUM_SIGNALS_TO_PRESERVE (sizeof(_signals_to_preserve) / sizeof(_signals_to_preserve[0]))
76
77#if(defined(__FreeBSD_version) && (__FreeBSD_version < 800071)) || (defined(OpenBSD) && (OpenBSD < 201305)) \
78 || defined(__SUNOS__)
79static int dprintf(int fd, const char *fmt, ...) __attribute__((format(printf, 2, 3)))
80{
81 va_list ap;
82 FILE *f = fdopen(fd, "a");
83 va_start(ap, fmt);
84 int rc = vfprintf(f, fmt, ap);
85 fclose(f);
86 va_end(ap);
87 return rc;
88}
89#endif
90
91#if !defined(__APPLE__) && !defined(_WIN32)
92static void _dt_sigsegv_handler(int param)
93{
94 // Sentry's crash handler runs first and chains here. If it already produced a
95 // gdb backtrace (attached to the crash report), don't run gdb a second time;
96 // just pass the signal on to the original handler.
98 {
100 return;
101 }
102
103 pid_t pid;
104 gchar *name_used;
105 int fout;
106 gboolean delete_file = FALSE;
107
108 if((fout = g_file_open_tmp("ansel_bt_XXXXXX.txt", &name_used, NULL)) == -1)
109 fout = STDOUT_FILENO; // just print everything to stdout
110
111 dprintf(fout, "this is %s reporting a segfault:\n\n", darktable_package_string);
112
113 if(fout != STDOUT_FILENO) close(fout);
114
115 gchar *pid_arg = g_strdup_printf("%d", (int)getpid());
116 gchar *exe_arg = g_strdup_printf("/proc/%s/exe", pid_arg);
117 gchar *log_file_arg = g_strdup_printf("set logging file %s", name_used);
118 const char *log_overwrite_arg = "set logging overwrite on";
119 const char *log_redirect_arg = "set logging redirect on";
120 const char *log_enabled_arg = "set logging enabled on";
121 const char *pagination_arg = "set pagination off";
122 const char *confirm_arg = "set confirm off";
123 const char *where_arg = "where";
124 const char *current_bt_arg = "bt full";
125 const char *current_thread_arg = "thread";
126 const char *info_registers_arg = "info registers";
127 const char *disassemble_pc_arg = "x/16i $pc";
128 const char *stack_words_arg = "x/16gx $sp";
129 const char *sharedlibrary_arg = "info sharedlibrary";
130 const char *mappings_arg = "info proc mappings";
131 const char *info_threads_arg = "info threads";
132 const char *thread_bt_arg = "thread apply all bt full";
133 const char *separator_a_arg = "echo \\n=========\\n\\n";
134 const char *separator_b_arg = "echo \\n=========\\n";
135 const char *separator_c_arg = "echo \\n========= current thread =========\\n";
136 const char *separator_d_arg = "echo \\n========= registers =========\\n";
137 const char *separator_e_arg = "echo \\n========= disassembly =========\\n";
138 const char *separator_f_arg = "echo \\n========= stack =========\\n";
139 const char *separator_g_arg = "echo \\n========= shared libraries =========\\n";
140 const char *separator_h_arg = "echo \\n========= mappings =========\\n";
141
142 if((pid = fork()) != -1)
143 {
144 if(pid)
145 {
146#ifdef __linux__
147 // Allow the child to ptrace us
148 prctl(PR_SET_PTRACER, pid, 0, 0, 0);
149#endif
150 waitpid(pid, NULL, 0);
151 g_printerr("backtrace written to %s\n", name_used);
152 }
153 else
154 {
155 if(fout != STDOUT_FILENO)
156 {
157 const int log_fd = open(name_used, O_WRONLY | O_APPEND);
158 if(log_fd != -1)
159 {
160 dup2(log_fd, STDOUT_FILENO);
161 dup2(log_fd, STDERR_FILENO);
162 close(log_fd);
163 }
164 }
165
166 if(execlp("gdb", "gdb", exe_arg, pid_arg, "-batch", "-ex", pagination_arg, "-ex", confirm_arg, "-ex",
167 log_file_arg, "-ex", log_overwrite_arg, "-ex", log_redirect_arg, "-ex", log_enabled_arg, "-ex",
168 where_arg, "-ex", separator_c_arg, "-ex", current_thread_arg, "-ex", current_bt_arg, "-ex",
169 separator_d_arg, "-ex", info_registers_arg, "-ex", separator_e_arg, "-ex", disassemble_pc_arg,
170 "-ex", separator_f_arg, "-ex", stack_words_arg, "-ex", separator_g_arg, "-ex",
171 sharedlibrary_arg, "-ex", separator_h_arg, "-ex", mappings_arg, "-ex", separator_a_arg, "-ex",
172 info_threads_arg, "-ex", separator_b_arg, "-ex", thread_bt_arg, NULL))
173 {
174 delete_file = TRUE;
175 g_printerr("an error occurred while trying to execute gdb. please check if gdb is installed on your "
176 "system.\n");
177 }
178 }
179 }
180 else
181 {
182 delete_file = TRUE;
183 g_printerr("an error occurred while trying to execute gdb.\n");
184 }
185
186 if(delete_file) g_unlink(name_used);
187 dt_free(pid_arg);
188 dt_free(exe_arg);
189 dt_free(log_file_arg);
190 dt_free(name_used);
191
192 /* pass it further to the old handler*/
194}
195#endif
196
198
199#if defined(_WIN32)
200
201static LONG WINAPI dt_toplevel_exception_handler(PEXCEPTION_POINTERS pExceptionInfo)
202{
203 gchar *name_used;
204 int fout;
205 BOOL ok;
206
207 // Find a filename for the backtrace file
208 if((fout = g_file_open_tmp("ansel_bt_XXXXXX.txt", &name_used, NULL)) == -1)
209 fout = STDOUT_FILENO; // just print everything to stdout
210
211 FILE *fd = fdopen(fout, "wb");
212 fprintf(fd, "this is %s reporting an exception:\n\n", darktable_package_string);
213 fclose(fd);
214
215 if(fout != STDOUT_FILENO) close(fout);
216
217
218 // Set up logfile name
219 ok = ExcHndlSetLogFileNameA(name_used);
220 if(!ok)
221 {
222 g_printerr("backtrace logfile cannot be set to %s\n", name_used);
223 }
224 else
225 {
226 gchar *exception_message = g_strdup_printf("An unhandled exception occurred.\nBacktrace will be written to: %s "
227 "after you click on the OK button.\nIf you report this issue, "
228 "please share this backtrace with the developers.\n",
229 name_used);
230 wchar_t *wexception_message = g_utf8_to_utf16(exception_message, -1, NULL, NULL, NULL);
231 MessageBoxW(0, wexception_message, L"Error!", MB_OK);
232 dt_free(exception_message);
233 dt_free(wexception_message);
234 }
235
236 dt_free(name_used);
237
238 // finally call the original exception handler (which should be drmingw's exception handler)
239 return _dt_exceptionfilter_old_handler(pExceptionInfo);
240}
241
242void dt_set_unhandled_exception_handler_win()
243{
244 // Set up drming's exception handler
245 ExcHndlInit();
246}
247#endif // defined(_WIN32)
248
249
251{
253
255
257 {
258 // save original handlers
259 for(int i = 0; i < _NUM_SIGNALS_TO_PRESERVE; i++)
260 {
261 const int signum = _signals_to_preserve[i];
262
263 prev = signal(signum, SIG_DFL);
264
265 if(SIG_ERR == prev) prev = SIG_DFL;
266
267 _orig_sig_handlers[i] = prev;
268 }
269 }
270
271 // restore handlers
272 for(int i = 0; i < _NUM_SIGNALS_TO_PRESERVE; i++)
273 {
274 const int signum = _signals_to_preserve[i];
275
276 (void)signal(signum, _orig_sig_handlers[i]);
277 }
278
279#if !defined(__APPLE__) && !defined(_WIN32)
280 // now, set our SIGSEGV handler.
281 // FIXME: what about SIGABRT?
282 prev = signal(SIGSEGV, &_dt_sigsegv_handler);
283
284 if(SIG_ERR != prev)
285 {
286 // we want the most original previous signal handler.
288 }
289 else
290 {
291 const int errsv = errno;
292 fprintf(stderr, "[dt_set_signal_handlers] error: signal(SIGSEGV) returned SIG_ERR: %i (%s)\n", errsv,
293 strerror(errsv));
294 }
295#elif !defined(__APPLE__)
296 /*
297 Set up exception handler for backtrace on Windows
298 Works when there is NO SIGSEGV handler installed
299
300 SetUnhandledExceptionFilter handler must be saved on the first invocation
301 as GraphicsMagick is overwriting SetUnhandledExceptionFilter and all other signals in InitializeMagick()
302 Eventually InitializeMagick() should be fixed upstream not to ignore existing exception handlers
303 */
304
305 dt_set_unhandled_exception_handler_win();
307 {
308 // Save UnhandledExceptionFilter handler which just has been set up
309 // This should be drmingw's exception handler
310 _dt_exceptionfilter_old_handler = SetUnhandledExceptionFilter(dt_toplevel_exception_handler);
311 }
312 // Restore our UnhandledExceptionFilter handler no matter what GM is doing
313 SetUnhandledExceptionFilter(dt_toplevel_exception_handler);
314
315#endif
316}
317
318// clang-format off
319// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
320// vim: shiftwidth=2 expandtab tabstop=2 cindent
321// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
322// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
const dt_aligned_pixel_t f
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
const char darktable_package_string[]
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Enable aggressive floating-point arithmetic optimizations, in denormals handling. Set through user pr...
Definition darktable.h:524
#define dt_free(ptr)
Definition darktable.h:456
#define LONG
Definition imageio_dng.h:52
gboolean dt_sentry_backtrace_captured(void)
Definition sentry.c:487
const float const float param
void dt_set_signal_handlers()
static int _times_handlers_were_set
static dt_signal_handler_t * _orig_sig_handlers[(sizeof(_signals_to_preserve)/sizeof(_signals_to_preserve[0]))]
#define _NUM_SIGNALS_TO_PRESERVE
defined (_WIN32)
static dt_signal_handler_t * _dt_sigsegv_old_handler
static const int _signals_to_preserve[]
static void _dt_sigsegv_handler(int param)
void() dt_signal_handler_t(int)