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