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 // Sentry's crash handler runs first and chains here. If it already captured a
204 // (locally symbolicated) backtrace attached to the crash report, don't write
205 // a second one nor pop the dialog; just pass the exception on.
207 return _dt_exceptionfilter_old_handler(pExceptionInfo);
208
209 gchar *name_used;
210 int fout;
211 BOOL ok;
212
213 // Find a filename for the backtrace file
214 if((fout = g_file_open_tmp("ansel_bt_XXXXXX.txt", &name_used, NULL)) == -1)
215 fout = STDOUT_FILENO; // just print everything to stdout
216
217 FILE *fd = fdopen(fout, "wb");
218 fprintf(fd, "this is %s reporting an exception:\n\n", darktable_package_string);
219 fclose(fd);
220
221 if(fout != STDOUT_FILENO) close(fout);
222
223
224 // Set up logfile name
225 ok = ExcHndlSetLogFileNameA(name_used);
226 if(!ok)
227 {
228 g_printerr("backtrace logfile cannot be set to %s\n", name_used);
229 }
230 else
231 {
232 gchar *exception_message = g_strdup_printf("An unhandled exception occurred.\nBacktrace will be written to: %s "
233 "after you click on the OK button.\nIf you report this issue, "
234 "please share this backtrace with the developers.\n",
235 name_used);
236 wchar_t *wexception_message = g_utf8_to_utf16(exception_message, -1, NULL, NULL, NULL);
237 MessageBoxW(0, wexception_message, L"Error!", MB_OK);
238 dt_free(exception_message);
239 dt_free(wexception_message);
240 }
241
242 dt_free(name_used);
243
244 // finally call the original exception handler (which should be drmingw's exception handler)
245 return _dt_exceptionfilter_old_handler(pExceptionInfo);
246}
247
248void dt_set_unhandled_exception_handler_win()
249{
250 // Set up drming's exception handler
251 ExcHndlInit();
252}
253#endif // defined(_WIN32)
254
255
257{
259
261
263 {
264 // save original handlers
265 for(int i = 0; i < _NUM_SIGNALS_TO_PRESERVE; i++)
266 {
267 const int signum = _signals_to_preserve[i];
268
269 prev = signal(signum, SIG_DFL);
270
271 if(SIG_ERR == prev) prev = SIG_DFL;
272
273 _orig_sig_handlers[i] = prev;
274 }
275 }
276
277 // restore handlers
278 for(int i = 0; i < _NUM_SIGNALS_TO_PRESERVE; i++)
279 {
280 const int signum = _signals_to_preserve[i];
281
282 (void)signal(signum, _orig_sig_handlers[i]);
283 }
284
285#if !defined(__APPLE__) && !defined(_WIN32)
286 // now, set our SIGSEGV handler.
287 // FIXME: what about SIGABRT?
288 prev = signal(SIGSEGV, &_dt_sigsegv_handler);
289
290 if(SIG_ERR != prev)
291 {
292 // we want the most original previous signal handler.
294 }
295 else
296 {
297 const int errsv = errno;
298 fprintf(stderr, "[dt_set_signal_handlers] error: signal(SIGSEGV) returned SIG_ERR: %i (%s)\n", errsv,
299 strerror(errsv));
300 }
301#elif !defined(__APPLE__)
302 /*
303 Set up exception handler for backtrace on Windows
304 Works when there is NO SIGSEGV handler installed
305
306 SetUnhandledExceptionFilter handler must be saved on the first invocation
307 as GraphicsMagick is overwriting SetUnhandledExceptionFilter and all other signals in InitializeMagick()
308 Eventually InitializeMagick() should be fixed upstream not to ignore existing exception handlers
309 */
310
311 dt_set_unhandled_exception_handler_win();
313 {
314 // Save UnhandledExceptionFilter handler which just has been set up
315 // This should be drmingw's exception handler
316 _dt_exceptionfilter_old_handler = SetUnhandledExceptionFilter(dt_toplevel_exception_handler);
317 }
318 // Restore our UnhandledExceptionFilter handler no matter what GM is doing
319 SetUnhandledExceptionFilter(dt_toplevel_exception_handler);
320
321#endif
322}
323
324// clang-format off
325// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
326// vim: shiftwidth=2 expandtab tabstop=2 cindent
327// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
328// 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:546
#define dt_free(ptr)
Definition darktable.h:478
#define LONG
Definition imageio_dng.h:52
gboolean dt_sentry_backtrace_captured(void)
Definition sentry.c:628
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)