1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis
3 *
4 * gimpbacktrace-linux.c
5 * Copyright (C) 2018 Ell
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21
22 #define _GNU_SOURCE
23
24
25 #include "config.h"
26
27 #include <gio/gio.h>
28
29 #include "gimpbacktrace-backend.h"
30
31
32 #ifdef GIMP_BACKTRACE_BACKEND_LINUX
33
34
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/syscall.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <dirent.h>
41 #include <signal.h>
42 #include <execinfo.h>
43 #include <dlfcn.h>
44 #include <string.h>
45 #include <stdio.h>
46
47 #ifdef HAVE_LIBBACKTRACE
48 #include <backtrace.h>
49 #endif
50
51 #ifdef HAVE_LIBUNWIND
52 #define UNW_LOCAL_ONLY
53 #include <libunwind.h>
54 #endif
55
56 #include "core-types.h"
57
58 #include "gimpbacktrace.h"
59
60
61 #define MAX_N_THREADS 256
62 #define MAX_N_FRAMES 256
63 #define MAX_THREAD_NAME_SIZE 32
64 #define N_SKIPPED_FRAMES 2
65 #define MAX_WAIT_TIME (G_TIME_SPAN_SECOND / 20)
66 #define BACKTRACE_SIGNAL SIGUSR1
67
68
69 typedef struct _GimpBacktraceThread GimpBacktraceThread;
70
71
72 struct _GimpBacktraceThread
73 {
74 pid_t tid;
75 gchar name[MAX_THREAD_NAME_SIZE];
76 gchar state;
77
78 guintptr frames[MAX_N_FRAMES];
79 gint n_frames;
80 };
81
82 struct _GimpBacktrace
83 {
84 GimpBacktraceThread *threads;
85 gint n_threads;
86 };
87
88
89 /* local function prototypes */
90
91 static inline gint gimp_backtrace_normalize_frame (GimpBacktrace *backtrace,
92 gint thread,
93 gint frame);
94
95 static gint gimp_backtrace_enumerate_threads (gboolean include_current_thread,
96 pid_t *threads,
97 gint size);
98 static void gimp_backtrace_read_thread_name (pid_t tid,
99 gchar *name,
100 gint size);
101 static gchar gimp_backtrace_read_thread_state (pid_t tid);
102
103 static void gimp_backtrace_signal_handler (gint signum);
104
105
106 /* static variables */
107
108 static GMutex mutex;
109 static gint n_initializations;
110 static gboolean initialized;
111 static struct sigaction orig_action;
112 static pid_t blacklisted_threads[MAX_N_THREADS];
113 static gint n_blacklisted_threads;
114 static GimpBacktrace *handler_backtrace;
115 static gint handler_n_remaining_threads;
116 static gint handler_lock;
117
118 #ifdef HAVE_LIBBACKTRACE
119 static struct backtrace_state *backtrace_state;
120 #endif
121
122 static const gchar * const blacklisted_thread_names[] =
123 {
124 "gmain",
125 "threaded-ml"
126 };
127
128
129 /* private functions */
130
131
132 static inline gint
gimp_backtrace_normalize_frame(GimpBacktrace * backtrace,gint thread,gint frame)133 gimp_backtrace_normalize_frame (GimpBacktrace *backtrace,
134 gint thread,
135 gint frame)
136 {
137 if (frame >= 0)
138 return frame + N_SKIPPED_FRAMES;
139 else
140 return backtrace->threads[thread].n_frames + frame;
141 }
142
143 static gint
gimp_backtrace_enumerate_threads(gboolean include_current_thread,pid_t * threads,gint size)144 gimp_backtrace_enumerate_threads (gboolean include_current_thread,
145 pid_t *threads,
146 gint size)
147 {
148 DIR *dir;
149 struct dirent *dirent;
150 pid_t tid;
151 gint n_threads;
152
153 dir = opendir ("/proc/self/task");
154
155 if (! dir)
156 return 0;
157
158 tid = syscall (SYS_gettid);
159
160 n_threads = 0;
161
162 while (n_threads < size && (dirent = readdir (dir)))
163 {
164 pid_t id = g_ascii_strtoull (dirent->d_name, NULL, 10);
165
166 if (id)
167 {
168 if (! include_current_thread && id == tid)
169 id = 0;
170 }
171
172 if (id)
173 {
174 gint i;
175
176 for (i = 0; i < n_blacklisted_threads; i++)
177 {
178 if (id == blacklisted_threads[i])
179 {
180 id = 0;
181
182 break;
183 }
184 }
185 }
186
187 if (id)
188 threads[n_threads++] = id;
189 }
190
191 closedir (dir);
192
193 return n_threads;
194 }
195
196 static void
gimp_backtrace_read_thread_name(pid_t tid,gchar * name,gint size)197 gimp_backtrace_read_thread_name (pid_t tid,
198 gchar *name,
199 gint size)
200 {
201 gchar filename[64];
202 gint fd;
203
204 if (size <= 0)
205 return;
206
207 name[0] = '\0';
208
209 g_snprintf (filename, sizeof (filename),
210 "/proc/self/task/%llu/comm",
211 (unsigned long long) tid);
212
213 fd = open (filename, O_RDONLY);
214
215 if (fd >= 0)
216 {
217 gint n = read (fd, name, size);
218
219 if (n > 0)
220 name[n - 1] = '\0';
221
222 close (fd);
223 }
224 }
225
226 static gchar
gimp_backtrace_read_thread_state(pid_t tid)227 gimp_backtrace_read_thread_state (pid_t tid)
228 {
229 gchar buffer[64];
230 gint fd;
231 gchar state = '\0';
232
233 g_snprintf (buffer, sizeof (buffer),
234 "/proc/self/task/%llu/stat",
235 (unsigned long long) tid);
236
237 fd = open (buffer, O_RDONLY);
238
239 if (fd >= 0)
240 {
241 gint n = read (fd, buffer, sizeof (buffer));
242
243 if (n > 0)
244 buffer[n - 1] = '\0';
245
246 sscanf (buffer, "%*d %*s %c", &state);
247
248 close (fd);
249 }
250
251 return state;
252 }
253
254 static void
gimp_backtrace_signal_handler(gint signum)255 gimp_backtrace_signal_handler (gint signum)
256 {
257 GimpBacktrace *curr_backtrace;
258 gint lock;
259
260 do
261 {
262 lock = g_atomic_int_get (&handler_lock);
263
264 if (lock < 0)
265 continue;
266 }
267 while (! g_atomic_int_compare_and_exchange (&handler_lock, lock, lock + 1));
268
269 curr_backtrace = g_atomic_pointer_get (&handler_backtrace);
270
271 if (curr_backtrace)
272 {
273 pid_t tid = syscall (SYS_gettid);
274 gint i;
275
276 for (i = 0; i < curr_backtrace->n_threads; i++)
277 {
278 GimpBacktraceThread *thread = &curr_backtrace->threads[i];
279
280 if (thread->tid == tid)
281 {
282 thread->n_frames = backtrace ((gpointer *) thread->frames,
283 MAX_N_FRAMES);
284
285 g_atomic_int_dec_and_test (&handler_n_remaining_threads);
286
287 break;
288 }
289 }
290 }
291
292 g_atomic_int_dec_and_test (&handler_lock);
293 }
294
295
296 /* public functions */
297
298
299 void
gimp_backtrace_init(void)300 gimp_backtrace_init (void)
301 {
302 #ifdef HAVE_LIBBACKTRACE
303 backtrace_state = backtrace_create_state (NULL, 0, NULL, NULL);
304 #endif
305 }
306
307 gboolean
gimp_backtrace_start(void)308 gimp_backtrace_start (void)
309 {
310 g_mutex_lock (&mutex);
311
312 if (n_initializations == 0)
313 {
314 struct sigaction action = {};
315
316 action.sa_handler = gimp_backtrace_signal_handler;
317 action.sa_flags = SA_RESTART;
318
319 sigemptyset (&action.sa_mask);
320
321 if (sigaction (BACKTRACE_SIGNAL, &action, &orig_action) == 0)
322 {
323 pid_t *threads;
324 gint n_threads;
325 gint i;
326
327 n_blacklisted_threads = 0;
328
329 threads = g_new (pid_t, MAX_N_THREADS);
330
331 n_threads = gimp_backtrace_enumerate_threads (TRUE,
332 threads, MAX_N_THREADS);
333
334 for (i = 0; i < n_threads; i++)
335 {
336 gchar name[MAX_THREAD_NAME_SIZE];
337 gint j;
338
339 gimp_backtrace_read_thread_name (threads[i],
340 name, MAX_THREAD_NAME_SIZE);
341
342 for (j = 0; j < G_N_ELEMENTS (blacklisted_thread_names); j++)
343 {
344 if (! strcmp (name, blacklisted_thread_names[j]))
345 {
346 blacklisted_threads[n_blacklisted_threads++] = threads[i];
347 }
348 }
349 }
350
351 g_free (threads);
352
353 initialized = TRUE;
354 }
355 }
356
357 n_initializations++;
358
359 g_mutex_unlock (&mutex);
360
361 return initialized;
362 }
363
364 void
gimp_backtrace_stop(void)365 gimp_backtrace_stop (void)
366 {
367 g_return_if_fail (n_initializations > 0);
368
369 g_mutex_lock (&mutex);
370
371 n_initializations--;
372
373 if (n_initializations == 0 && initialized)
374 {
375 if (sigaction (BACKTRACE_SIGNAL, &orig_action, NULL) < 0)
376 g_warning ("failed to restore origianl backtrace signal handler");
377
378 initialized = FALSE;
379 }
380
381 g_mutex_unlock (&mutex);
382 }
383
384 GimpBacktrace *
gimp_backtrace_new(gboolean include_current_thread)385 gimp_backtrace_new (gboolean include_current_thread)
386 {
387 GimpBacktrace *backtrace;
388 pid_t pid;
389 pid_t *threads;
390 gint n_threads;
391 gint64 start_time;
392 gint i;
393
394 if (! initialized)
395 return NULL;
396
397 pid = getpid ();
398
399 threads = g_new (pid_t, MAX_N_THREADS);
400
401 n_threads = gimp_backtrace_enumerate_threads (include_current_thread,
402 threads, MAX_N_THREADS);
403
404 if (n_threads == 0)
405 {
406 g_free (threads);
407
408 return NULL;
409 }
410
411 g_mutex_lock (&mutex);
412
413 backtrace = g_slice_new (GimpBacktrace);
414
415 backtrace->threads = g_new (GimpBacktraceThread, n_threads);
416 backtrace->n_threads = n_threads;
417
418 while (! g_atomic_int_compare_and_exchange (&handler_lock, 0, -1));
419
420 g_atomic_pointer_set (&handler_backtrace, backtrace);
421 g_atomic_int_set (&handler_n_remaining_threads, n_threads);
422
423 g_atomic_int_set (&handler_lock, 0);
424
425 for (i = 0; i < n_threads; i++)
426 {
427 GimpBacktraceThread *thread = &backtrace->threads[i];
428
429 thread->tid = threads[i];
430 thread->n_frames = 0;
431
432 gimp_backtrace_read_thread_name (thread->tid,
433 thread->name, MAX_THREAD_NAME_SIZE);
434
435 thread->state = gimp_backtrace_read_thread_state (thread->tid);
436
437 syscall (SYS_tgkill, pid, threads[i], BACKTRACE_SIGNAL);
438 }
439
440 g_free (threads);
441
442 start_time = g_get_monotonic_time ();
443
444 while (g_atomic_int_get (&handler_n_remaining_threads) > 0)
445 {
446 gint64 time = g_get_monotonic_time ();
447
448 if (time - start_time > MAX_WAIT_TIME)
449 break;
450
451 g_usleep (1000);
452 }
453
454 while (! g_atomic_int_compare_and_exchange (&handler_lock, 0, -1));
455
456 g_atomic_pointer_set (&handler_backtrace, NULL);
457
458 g_atomic_int_set (&handler_lock, 0);
459
460 #if 0
461 if (handler_n_remaining_threads > 0)
462 {
463 gint j = 0;
464
465 for (i = 0; i < n_threads; i++)
466 {
467 if (backtrace->threads[i].n_frames == 0)
468 {
469 if (n_blacklisted_threads < MAX_N_THREADS)
470 {
471 blacklisted_threads[n_blacklisted_threads++] =
472 backtrace->threads[i].tid;
473 }
474 }
475 else
476 {
477 if (j < i)
478 backtrace->threads[j] = backtrace->threads[i];
479
480 j++;
481 }
482 }
483
484 n_threads = j;
485 }
486 #endif
487
488 g_mutex_unlock (&mutex);
489
490 if (n_threads == 0)
491 {
492 gimp_backtrace_free (backtrace);
493
494 return NULL;
495 }
496
497 return backtrace;
498 }
499
500 void
gimp_backtrace_free(GimpBacktrace * backtrace)501 gimp_backtrace_free (GimpBacktrace *backtrace)
502 {
503 if (! backtrace)
504 return;
505
506 g_free (backtrace->threads);
507
508 g_slice_free (GimpBacktrace, backtrace);
509 }
510
511 gint
gimp_backtrace_get_n_threads(GimpBacktrace * backtrace)512 gimp_backtrace_get_n_threads (GimpBacktrace *backtrace)
513 {
514 g_return_val_if_fail (backtrace != NULL, 0);
515
516 return backtrace->n_threads;
517 }
518
519 guintptr
gimp_backtrace_get_thread_id(GimpBacktrace * backtrace,gint thread)520 gimp_backtrace_get_thread_id (GimpBacktrace *backtrace,
521 gint thread)
522 {
523 g_return_val_if_fail (backtrace != NULL, 0);
524 g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0);
525
526 return backtrace->threads[thread].tid;
527 }
528
529 const gchar *
gimp_backtrace_get_thread_name(GimpBacktrace * backtrace,gint thread)530 gimp_backtrace_get_thread_name (GimpBacktrace *backtrace,
531 gint thread)
532 {
533 g_return_val_if_fail (backtrace != NULL, NULL);
534 g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, NULL);
535
536 if (backtrace->threads[thread].name[0])
537 return backtrace->threads[thread].name;
538 else
539 return NULL;
540 }
541
542 gboolean
gimp_backtrace_is_thread_running(GimpBacktrace * backtrace,gint thread)543 gimp_backtrace_is_thread_running (GimpBacktrace *backtrace,
544 gint thread)
545 {
546 g_return_val_if_fail (backtrace != NULL, FALSE);
547 g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, FALSE);
548
549 return backtrace->threads[thread].state == 'R';
550 }
551
552 gint
gimp_backtrace_find_thread_by_id(GimpBacktrace * backtrace,guintptr thread_id,gint thread_hint)553 gimp_backtrace_find_thread_by_id (GimpBacktrace *backtrace,
554 guintptr thread_id,
555 gint thread_hint)
556 {
557 pid_t tid = thread_id;
558 gint i;
559
560 g_return_val_if_fail (backtrace != NULL, -1);
561
562 if (thread_hint >= 0 &&
563 thread_hint < backtrace->n_threads &&
564 backtrace->threads[thread_hint].tid == tid)
565 {
566 return thread_hint;
567 }
568
569 for (i = 0; i < backtrace->n_threads; i++)
570 {
571 if (backtrace->threads[i].tid == tid)
572 return i;
573 }
574
575 return -1;
576 }
577
578 gint
gimp_backtrace_get_n_frames(GimpBacktrace * backtrace,gint thread)579 gimp_backtrace_get_n_frames (GimpBacktrace *backtrace,
580 gint thread)
581 {
582 g_return_val_if_fail (backtrace != NULL, 0);
583 g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0);
584
585 return MAX (backtrace->threads[thread].n_frames - N_SKIPPED_FRAMES, 0);
586 }
587
588 guintptr
gimp_backtrace_get_frame_address(GimpBacktrace * backtrace,gint thread,gint frame)589 gimp_backtrace_get_frame_address (GimpBacktrace *backtrace,
590 gint thread,
591 gint frame)
592 {
593 g_return_val_if_fail (backtrace != NULL, 0);
594 g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0);
595
596 frame = gimp_backtrace_normalize_frame (backtrace, thread, frame);
597
598 g_return_val_if_fail (frame >= N_SKIPPED_FRAMES &&
599 frame < backtrace->threads[thread].n_frames, 0);
600
601 return backtrace->threads[thread].frames[frame];
602 }
603
604 #ifdef HAVE_LIBBACKTRACE
605 static void
gimp_backtrace_syminfo_callback(GimpBacktraceAddressInfo * info,guintptr pc,const gchar * symname,guintptr symval,guintptr symsize)606 gimp_backtrace_syminfo_callback (GimpBacktraceAddressInfo *info,
607 guintptr pc,
608 const gchar *symname,
609 guintptr symval,
610 guintptr symsize)
611 {
612 if (symname)
613 g_strlcpy (info->symbol_name, symname, sizeof (info->symbol_name));
614
615 info->symbol_address = symval;
616 }
617
618 static gint
gimp_backtrace_pcinfo_callback(GimpBacktraceAddressInfo * info,guintptr pc,const gchar * filename,gint lineno,const gchar * function)619 gimp_backtrace_pcinfo_callback (GimpBacktraceAddressInfo *info,
620 guintptr pc,
621 const gchar *filename,
622 gint lineno,
623 const gchar *function)
624 {
625 if (function)
626 g_strlcpy (info->symbol_name, function, sizeof (info->symbol_name));
627
628 if (filename)
629 g_strlcpy (info->source_file, filename, sizeof (info->source_file));
630
631 info->source_line = lineno;
632
633 return 0;
634 }
635 #endif /* HAVE_LIBBACKTRACE */
636
637 gboolean
gimp_backtrace_get_address_info(guintptr address,GimpBacktraceAddressInfo * info)638 gimp_backtrace_get_address_info (guintptr address,
639 GimpBacktraceAddressInfo *info)
640 {
641 Dl_info dl_info;
642 gboolean result = FALSE;
643
644 g_return_val_if_fail (info != NULL, FALSE);
645
646 info->object_name[0] = '\0';
647
648 info->symbol_name[0] = '\0';
649 info->symbol_address = 0;
650
651 info->source_file[0] = '\0';
652 info->source_line = 0;
653
654 if (dladdr ((gpointer) address, &dl_info))
655 {
656 if (dl_info.dli_fname)
657 {
658 g_strlcpy (info->object_name, dl_info.dli_fname,
659 sizeof (info->object_name));
660 }
661
662 if (dl_info.dli_sname)
663 {
664 g_strlcpy (info->symbol_name, dl_info.dli_sname,
665 sizeof (info->symbol_name));
666 }
667
668 info->symbol_address = (guintptr) dl_info.dli_saddr;
669
670 result = TRUE;
671 }
672
673 #ifdef HAVE_LIBBACKTRACE
674 if (backtrace_state)
675 {
676 backtrace_syminfo (
677 backtrace_state, address,
678 (backtrace_syminfo_callback) gimp_backtrace_syminfo_callback,
679 NULL,
680 info);
681
682 backtrace_pcinfo (
683 backtrace_state, address,
684 (backtrace_full_callback) gimp_backtrace_pcinfo_callback,
685 NULL,
686 info);
687
688 result = TRUE;
689 }
690 #endif /* HAVE_LIBBACKTRACE */
691
692 #ifdef HAVE_LIBUNWIND
693 /* we use libunwind to get the symbol name, when available, even if dladdr() or
694 * libbacktrace already found one, since it provides more descriptive names in
695 * some cases, and, in particular, full symbol names for C++ lambdas.
696 *
697 * note that, in some cases, this can result in a discrepancy between the
698 * symbol name, and the corresponding source location.
699 */
700 #if 0
701 if (! info->symbol_name[0])
702 #endif
703 {
704 unw_context_t context = {};
705 unw_cursor_t cursor;
706 unw_word_t offset;
707
708 if (unw_init_local (&cursor, &context) == 0 &&
709 unw_set_reg (&cursor, UNW_REG_IP, address) == 0 &&
710 unw_get_proc_name (&cursor,
711 info->symbol_name, sizeof (info->symbol_name),
712 &offset) == 0)
713 {
714 info->symbol_address = address - offset;
715
716 result = TRUE;
717 }
718 }
719 #endif /* HAVE_LIBUNWIND */
720
721 return result;
722 }
723
724
725 #endif /* GIMP_BACKTRACE_BACKEND_LINUX */
726