31#include <glib/gstdio.h>
52#ifndef WIN32_LEAN_AND_MEAN
53#define WIN32_LEAN_AND_MEAN
66#define DT_SENTRY_ENABLED_KEY "sentry/enabled"
67#define DT_SENTRY_ASKED_KEY "sentry/consent_asked"
68#define DT_SENTRY_CLEAN_SESSIONS_KEY "sentry/clean_sessions"
69#define DT_SENTRY_LAST_SESSION_KEY "sentry/last_session_seconds"
70#define DT_SENTRY_TOTAL_SESSION_KEY "sentry/total_session_seconds"
72static gboolean _sentry_inited =
FALSE;
79#define DT_SENTRY_MAX_CRASH_OBSERVERS 4
81static int _crash_observer_count = 0;
85 if(
IS_NULL_PTR(observer) || _crash_observer_count >= DT_SENTRY_MAX_CRASH_OBSERVERS)
return;
86 _crash_observers[_crash_observer_count++] = observer;
90static void _sentry_notify_crash_observers(
const char *backtrace, gsize backtrace_len)
92 for(
int i = 0;
i < _crash_observer_count;
i++)
93 if(_crash_observers[
i]) _crash_observers[
i](backtrace, backtrace_len);
98static volatile sig_atomic_t _sentry_backtrace_captured = 0;
103static GHashTable *_module_usage = NULL;
107static GMutex _processed_image_lock;
108static int32_t _processed_imgid = -1;
109static char _processed_pipeline[32] = { 0 };
112static volatile int _processed_image_count = 0;
116static double _sentry_session_seconds(
void)
119 return (dur > 0.0) ? dur : 0.0;
124static void _sentry_stamp_session_length(sentry_value_t event)
126 const double dur = _sentry_session_seconds();
129 sentry_value_t extra = sentry_value_get_by_key(event,
"extra");
130 if(sentry_value_is_null(extra))
132 extra = sentry_value_new_object();
133 sentry_value_set_by_key(event,
"extra", extra);
135 sentry_value_set_by_key(extra,
"session_seconds", sentry_value_new_double(dur));
136 sentry_value_set_by_key(extra,
"images_processed", sentry_value_new_int32(_processed_image_count));
141 snprintf(buf,
sizeof(buf),
"%.0f", dur);
143 snprintf(ibuf,
sizeof(ibuf),
"%d", _processed_image_count);
144 sentry_value_t tags = sentry_value_get_by_key(event,
"tags");
145 if(sentry_value_is_null(tags))
147 tags = sentry_value_new_object();
148 sentry_value_set_by_key(event,
"tags", tags);
150 sentry_value_set_by_key(tags,
"session_seconds", sentry_value_new_string(buf));
151 sentry_value_set_by_key(tags,
"images_processed", sentry_value_new_string(ibuf));
156static sentry_value_t _sentry_before_send(sentry_value_t event,
void *hint,
void *user_data)
158 _sentry_stamp_session_length(event);
162#if defined(__linux__)
167static char *_sentry_capture_gdb_backtrace(gsize *len)
170 const int fd = g_file_open_tmp(
"ansel_sentry_bt_XXXXXX.txt", &
name, NULL);
171 if(fd == -1)
return NULL;
174 gchar *pid_arg = g_strdup_printf(
"%d", (
int)getpid());
175 gchar *exe_arg = g_strdup_printf(
"/proc/%s/exe", pid_arg);
176 gchar *log_file_arg = g_strdup_printf(
"set logging file %s",
name);
178 char *contents = NULL;
179 const pid_t pid = fork();
183 execlp(
"gdb",
"gdb", exe_arg, pid_arg,
"-batch",
184 "-ex",
"set pagination off",
185 "-ex",
"set confirm off",
187 "-ex",
"set logging overwrite on",
188 "-ex",
"set logging redirect on",
189 "-ex",
"set logging enabled on",
190 "-ex",
"thread apply all bt full",
197 waitpid(pid, NULL, 0);
200 if(g_file_get_contents(
name, &contents, &
n, NULL))
214 g_free(log_file_arg);
221static void _sentry_module_name(DWORD64 addr,
char *
out,
size_t out_len)
223 g_strlcpy(
out,
"?", out_len);
225 if(GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
226 | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
227 (LPCSTR)(uintptr_t)addr, &mod)
231 if(GetModuleFileNameA(mod, path,
sizeof(path)))
233 const char *base = strrchr(path,
'\\');
234 g_strlcpy(
out, base ? base + 1 :
path, out_len);
245static char *_sentry_capture_windows_backtrace(
const sentry_ucontext_t *uctx, gsize *len)
250 const size_t n = sentry_unwind_stack_from_ucontext(uctx, frames, 128);
251 if(
n == 0)
return NULL;
253 const HANDLE
proc = GetCurrentProcess();
254 SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
257 SymInitialize(proc, NULL,
TRUE);
259 GString *
out = g_string_new(NULL);
260 g_string_append_printf(
out,
"this is %s reporting a crash (local DbgHelp backtrace, crashing thread):\n\n",
264 char symbuf[
sizeof(SYMBOL_INFO) + 512];
265 SYMBOL_INFO *sym = (SYMBOL_INFO *)symbuf;
266 memset(symbuf, 0,
sizeof(symbuf));
267 sym->SizeOfStruct =
sizeof(SYMBOL_INFO);
268 sym->MaxNameLen = 512;
270 for(
size_t i = 0;
i <
n;
i++)
272 const DWORD64 addr = (DWORD64)(uintptr_t)frames[
i];
274 char modname[MAX_PATH];
275 _sentry_module_name(addr, modname,
sizeof(modname));
277 const unsigned long long fno = (
unsigned long long)
i;
279 if(SymFromAddr(proc, addr, &disp, sym))
282 IMAGEHLP_LINE64 line;
283 memset(&line, 0,
sizeof(line));
284 line.SizeOfStruct =
sizeof(IMAGEHLP_LINE64);
285 if(SymGetLineFromAddr64(proc, addr, &line_disp, &line))
286 g_string_append_printf(
out,
"#%-2llu 0x%016llx %s+0x%llx (%s:%lu) [%s]\n", fno,
287 (
unsigned long long)addr, sym->Name, (
unsigned long long)disp,
288 line.FileName, (
unsigned long)line.LineNumber, modname);
290 g_string_append_printf(
out,
"#%-2llu 0x%016llx %s+0x%llx [%s]\n", fno, (
unsigned long long)addr,
291 sym->Name, (
unsigned long long)disp, modname);
295 g_string_append_printf(
out,
"#%-2llu 0x%016llx ? [%s]\n", fno, (
unsigned long long)addr, modname);
299 if(len) *len =
out->len;
307static sentry_value_t _sentry_on_crash(
const sentry_ucontext_t *uctx, sentry_value_t event,
void *user_data)
309 _sentry_stamp_session_length(event);
317#if defined(__linux__)
318 bt = _sentry_capture_gdb_backtrace(&bt_len);
323 sentry_attach_bytes(bt, bt_len,
"gdb-backtrace.txt");
327 _sentry_backtrace_captured = 1;
330 bt = _sentry_capture_windows_backtrace(uctx, &bt_len);
333 sentry_attach_bytes(bt, bt_len,
"windows-backtrace.txt");
336 _sentry_backtrace_captured = 1;
345 if(bt && bt_len > 0) _sentry_notify_crash_observers(bt, bt_len);
353 return _sentry_backtrace_captured != 0;
358 if(!_sentry_inited || !category || !
name || !*
name)
return;
361 _module_usage = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
365 char *
key = g_strdup_printf(
"%s/%s", category,
name);
366 const int count = GPOINTER_TO_INT(g_hash_table_lookup(_module_usage,
key)) + 1;
367 g_hash_table_insert(_module_usage,
key, GINT_TO_POINTER(count));
371 sentry_value_t obj = sentry_value_new_object();
374 g_hash_table_iter_init(&iter, _module_usage);
375 while(g_hash_table_iter_next(&iter, &
k, &
v))
376 sentry_value_set_by_key(obj, (
const char *)
k, sentry_value_new_int32(GPOINTER_TO_INT(
v)));
377 sentry_set_context(
"module_usage", obj);
382 if(!_sentry_inited || !img)
return;
383 const char *pl = pipeline ? pipeline :
"";
387 g_mutex_lock(&_processed_image_lock);
388 if(img->
id == _processed_imgid && !strcmp(pl, _processed_pipeline))
390 g_mutex_unlock(&_processed_image_lock);
393 _processed_imgid = img->
id;
394 g_strlcpy(_processed_pipeline, pl,
sizeof(_processed_pipeline));
395 _processed_image_count++;
396 g_mutex_unlock(&_processed_image_lock);
399 const char *dot = strrchr(img->
filename,
'.');
401 sentry_value_t
o = sentry_value_new_object();
402 sentry_value_set_by_key(
o,
"extension", sentry_value_new_string(dot ? dot + 1 :
""));
403 sentry_value_set_by_key(
o,
"pipeline", sentry_value_new_string(pl));
404 sentry_value_set_by_key(
o,
"raw", sentry_value_new_bool(
dt_image_is_raw(img)));
405 sentry_value_set_by_key(
o,
"ldr", sentry_value_new_bool(
dt_image_is_ldr(img)));
406 sentry_value_set_by_key(
o,
"hdr", sentry_value_new_bool(
dt_image_is_hdr(img)));
410 sentry_value_set_by_key(
o,
"needs_demosaic", sentry_value_new_bool(img->
dsc.
filters != 0));
411 sentry_value_set_by_key(
o,
"width", sentry_value_new_int32(img->
width));
412 sentry_value_set_by_key(
o,
"height", sentry_value_new_int32(img->
height));
413 sentry_set_context(
"processed_image",
o);
418static void _sentry_set_context(
void)
421 sentry_value_t device = sentry_value_new_object();
422 sentry_value_set_by_key(device,
"cpu_logical_cores", sentry_value_new_int32(g_get_num_processors()));
428 sentry_value_set_by_key(device,
"memory_gb", sentry_value_new_double(mem_gb));
432 sentry_value_set_by_key(device,
"opencl_enabled", sentry_value_new_bool(cl_enabled));
438 sentry_value_t gpus = sentry_value_new_list();
442 if(
name) sentry_value_append(gpus, sentry_value_new_string(
name));
444 sentry_value_set_by_key(device,
"opencl_devices", gpus);
450 sentry_set_context(
"device", device);
452#if !defined(_WIN32) && !defined(__APPLE__)
455 const char *session_type = g_getenv(
"XDG_SESSION_TYPE");
456 if(!session_type || !*session_type)
459 if(g_getenv(
"WAYLAND_DISPLAY"))
460 session_type =
"wayland";
461 else if(g_getenv(
"DISPLAY"))
462 session_type =
"x11";
464 if(session_type && *session_type) sentry_set_tag(
"display_server", session_type);
466 const char *desktop = g_getenv(
"XDG_CURRENT_DESKTOP");
467 if(!desktop || !*desktop) desktop = g_getenv(
"DESKTOP_SESSION");
468 if(desktop && *desktop) sentry_set_tag(
"desktop_environment", desktop);
473 GdkDisplay *display = gdk_display_get_default();
474 if(display) sentry_set_tag(
"gdk_backend", G_OBJECT_TYPE_NAME(display));
483 sentry_value_t scr = sentry_value_new_object();
484 sentry_value_set_by_key(scr,
"dpi", sentry_value_new_double(
dt_screen_dpi()));
486 sentry_value_set_by_key(scr,
"ppd", sentry_value_new_double(
dt_screen_ppd()));
490 if(win_w > 0 && win_h > 0)
492 sentry_value_set_by_key(scr,
"window_width", sentry_value_new_int32(win_w));
493 sentry_value_set_by_key(scr,
"window_height", sentry_value_new_int32(win_h));
497 snprintf(wbuf,
sizeof(wbuf),
"%dx%d", win_w, win_h);
498 sentry_set_tag(
"window_size", wbuf);
502 GdkDisplay *gdkdisp = gdk_display_get_default();
503 GdkMonitor *
mon = gdkdisp ? gdk_display_get_primary_monitor(gdkdisp) : NULL;
504 if(!
mon && gdkdisp && gdk_display_get_n_monitors(gdkdisp) > 0)
505 mon = gdk_display_get_monitor(gdkdisp, 0);
509 gdk_monitor_get_geometry(
mon, &geo);
510 sentry_value_set_by_key(scr,
"screen_width", sentry_value_new_int32(geo.width));
511 sentry_value_set_by_key(scr,
"screen_height", sentry_value_new_int32(geo.height));
512 sentry_value_set_by_key(scr,
"monitor_scale_factor",
513 sentry_value_new_int32(gdk_monitor_get_scale_factor(
mon)));
516 snprintf(sbuf,
sizeof(sbuf),
"%dx%d", geo.width, geo.height);
517 sentry_set_tag(
"screen_size", sbuf);
519 sentry_set_context(
"display", scr);
527 sentry_set_tag(
"asserts",
"disabled");
529 sentry_set_tag(
"asserts",
"enabled");
533 sentry_set_extra(
"build_cflags", sentry_value_new_string(
DT_BUILD_C_FLAGS));
534 sentry_set_tag(
"opencl", cl_enabled ?
"yes" :
"no");
547 sentry_value_t user = sentry_value_new_object();
548 sentry_value_set_by_key(user,
"id", sentry_value_new_string(
dt_install_id()));
549 sentry_set_user(user);
557 sentry_set_extra(
"clean_sessions_local",
558 sentry_value_new_int32(
dt_conf_get_int(DT_SENTRY_CLEAN_SESSIONS_KEY)));
563 sentry_set_extra(
"previous_session_seconds",
565 sentry_set_extra(
"total_session_seconds",
570static const char *_sentry_platform(
void)
574#elif defined(__APPLE__)
589 if(SENTRY_DSN[0] ==
'\0')
595 sentry_options_t *
options = sentry_options_new();
596 sentry_options_set_dsn(
options, SENTRY_DSN);
601 char *db_path = g_build_filename(cachedir,
"sentry-native", NULL);
602 sentry_options_set_database_path(
options, db_path);
611 sentry_options_set_release(
options, release);
624 char environment[128];
625 snprintf(environment,
sizeof(environment),
"%s-%s",
DT_BUILD_CHANNEL, _sentry_platform());
626 sentry_options_set_environment(
options, environment);
630 sentry_options_set_before_send(
options, _sentry_before_send, NULL);
633 sentry_options_set_on_crash(
options, _sentry_on_crash, NULL);
638 sentry_options_set_auto_session_tracking(
options, 1);
642 _sentry_inited =
TRUE;
643 _sentry_set_context();
654 if(!_sentry_inited)
return;
663 const int dur = (int)_sentry_session_seconds();
669 _sentry_inited =
FALSE;
673 g_hash_table_destroy(_module_usage);
674 _module_usage = NULL;
const char * dt_install_id(void)
const char * dt_session_id(void)
const dt_colormatrix_t dt_aligned_pixel_t out
int dt_conf_get_bool(const char *name)
void dt_conf_set_int(const char *name, int val)
void dt_conf_set_int64(const char *name, int64_t val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
int64_t dt_conf_get_int64(const char *name)
64-bit integer for name, clamped to its declared bounds.
gboolean dt_image_is_raw(const dt_image_t *img)
gboolean dt_image_is_hdr(const dt_image_t *img)
gboolean dt_image_is_monochrome(const dt_image_t *img)
gboolean dt_image_is_ldr(const dt_image_t *img)
const char darktable_commit_hash[]
const char darktable_package_string[]
const char darktable_package_version[]
int dt_get_num_openmp_threads(void)
Number of OpenMP threads the application decided to use.
size_t dt_get_total_mem(void)
void dt_loc_get_user_cache_dir(char *cachedir, size_t bufsize)
int32_t dt_get_debug_flags(void)
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
const char * dt_opencl_get_device_name(const int devid)
Human-readable device name, owned by the OpenCL module.
int dt_opencl_is_enabled(void)
int dt_opencl_get_num_devices(void)
Number of usable OpenCL devices; 0 when OpenCL is unavailable.
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
double dt_screen_dpi(void)
double dt_screen_dpi_factor(void)
gboolean dt_screen_metrics_probed(void)
double dt_screen_ppd(void)
gboolean dt_sentry_backtrace_captured(void)
void dt_sentry_record_module_usage(const char *category, const char *name)
void dt_sentry_shutdown(void)
void dt_sentry_init(const gboolean have_gui)
void dt_sentry_add_crash_observer(dt_sentry_crash_observer_t observer)
Register a function to be handed the backtrace when the process crashes.
void dt_sentry_set_processed_image(const struct dt_image_t *img, const char *pipeline)
void(* dt_sentry_crash_observer_t)(const char *backtrace, size_t backtrace_len)
Handed the crash backtrace from inside the crash handler.
static const char *const mon[12]
char filename[DT_MAX_FILENAME_LEN]
typedef double((*spd)(unsigned long int wavelength, double TempK))
double dt_get_start_wtime(void)
static double dt_get_wtime(void)