Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Gimp/app/core/   (Gimp Bildeditor Version 3.2.0©)  Datei vom 14.2.2026 mit Größe 16 kB image not shown  

Quelle  gimpbacktrace-linux.c   Sprache: C

 

/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis
 *
 * gimpbacktrace-linux.c
 * Copyright (C) 2018 Ell
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */



#define _GNU_SOURCE


#include "config.h"

#include <gio/gio.h>

#include "gimpbacktrace-backend.h"


#ifdef GIMP_BACKTRACE_BACKEND_LINUX


#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <signal.h>
#include <execinfo.h>
#include <dlfcn.h>
#include <string.h>
#include <stdio.h>

#ifdef HAVE_LIBBACKTRACE
#include <backtrace.h>
#endif

#ifdef HAVE_LIBUNWIND
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#endif

#include "core-types.h"

#include "gimpbacktrace.h"


#define MAX_N_THREADS        256
#define MAX_N_FRAMES         256
#define MAX_THREAD_NAME_SIZE 32
#define N_SKIPPED_FRAMES     2
#define MAX_WAIT_TIME        (G_TIME_SPAN_SECOND / 20)
#define BACKTRACE_SIGNAL     SIGUSR1


typedef struct _GimpBacktraceThread GimpBacktraceThread;


struct _GimpBacktraceThread
{
  pid_t    tid;
  gchar    name[MAX_THREAD_NAME_SIZE];
  gchar    state;

  guintptr frames[MAX_N_FRAMES];
  gint     n_frames;
};

struct _GimpBacktrace
{
  GimpBacktraceThread *threads;
  gint                 n_threads;
};


/*  local function prototypes  */

static inline gint   gimp_backtrace_normalize_frame   (GimpBacktrace *backtrace,
                                                       gint           thread,
                                                       gint           frame);

static gint          gimp_backtrace_enumerate_threads (gboolean       include_current_thread,
                                                       pid_t         *threads,
                                                       gint           size);
static void          gimp_backtrace_read_thread_name  (pid_t          tid,
                                                       gchar         *name,
                                                       gint           size);
static gchar         gimp_backtrace_read_thread_state (pid_t          tid);

static void          gimp_backtrace_signal_handler    (gint           signum);


/*  static variables  */

static GMutex            mutex;
static gint              n_initializations;
static gboolean          initialized;
static struct sigaction  orig_action;
static pid_t             blacklisted_threads[MAX_N_THREADS];
static gint              n_blacklisted_threads;
static GimpBacktrace    *handler_backtrace;
static gint              handler_n_remaining_threads;
static gint              handler_lock;

#ifdef HAVE_LIBBACKTRACE
static struct backtrace_state *backtrace_state;
#endif

static const gchar * const blacklisted_thread_names[] =
{
  "gmain",
  "threaded-ml"
};


/*  private functions  */


static inline gint
gimp_backtrace_normalize_frame (GimpBacktrace *backtrace,
                                gint           thread,
                                gint           frame)
{
  if (frame >= 0)
    return frame + N_SKIPPED_FRAMES;
  else
    return backtrace->threads[thread].n_frames + frame;
}

static gint
gimp_backtrace_enumerate_threads (gboolean  include_current_thread,
                                  pid_t    *threads,
                                  gint      size)
{
  DIR           *dir;
  struct dirent *dirent;
  pid_t          tid;
  gint           n_threads;

  dir = opendir ("/proc/self/task");

  if (! dir)
    return 0;

  tid = syscall (SYS_gettid);

  n_threads = 0;

  while (n_threads < size && (dirent = readdir (dir)))
    {
      pid_t id = g_ascii_strtoull (dirent->d_name, NULL, 10);

      if (id)
        {
          if (! include_current_thread && id == tid)
            id = 0;
        }

      if (id)
        {
          gint i;

          for (i = 0; i < n_blacklisted_threads; i++)
            {
              if (id == blacklisted_threads[i])
                {
                  id = 0;

                  break;
                }
            }
        }

      if (id)
        threads[n_threads++] = id;
    }

  closedir (dir);

  return n_threads;
}

static void
gimp_backtrace_read_thread_name (pid_t  tid,
                                 gchar *name,
                                 gint   size)
{
  gchar filename[64];
  gint  fd;

  if (size <= 0)
    return;

  name[0] = '\0';

  g_snprintf (filename, sizeof (filename),
              "/proc/self/task/%llu/comm",
              (unsigned long long) tid);

  fd = open (filename, O_RDONLY);

  if (fd >= 0)
    {
      gint n = read (fd, name, size);

      if (n > 0)
        name[n - 1] = '\0';

      close (fd);
    }
}

static gchar
gimp_backtrace_read_thread_state (pid_t tid)
{
  gchar buffer[64];
  gint  fd;
  gchar state = '\0';

  g_snprintf (buffer, sizeof (buffer),
              "/proc/self/task/%llu/stat",
              (unsigned long long) tid);

  fd = open (buffer, O_RDONLY);

  if (fd >= 0)
    {
      gint n = read (fd, buffer, sizeof (buffer));

      if (n > 0)
        buffer[n - 1] = '\0';

      sscanf (buffer, "%*d %*s %c", &state);

      close (fd);
    }

  return state;
}

static void
gimp_backtrace_signal_handler (gint signum)
{
  GimpBacktrace *curr_backtrace;
  gint           lock;

  do
    {
      lock = g_atomic_int_get (&handler_lock);

      if (lock < 0)
        continue;
    }
  while (! g_atomic_int_compare_and_exchange (&handler_lock, lock, lock + 1));

  curr_backtrace = g_atomic_pointer_get (&handler_backtrace);

  if (curr_backtrace)
    {
      pid_t tid = syscall (SYS_gettid);
      gint  i;

      for (i = 0; i < curr_backtrace->n_threads; i++)
        {
          GimpBacktraceThread *thread = &curr_backtrace->threads[i];

          if (thread->tid == tid)
            {
              thread->n_frames = backtrace ((gpointer *) thread->frames,
                                            MAX_N_FRAMES);

              g_atomic_int_dec_and_test (&handler_n_remaining_threads);

              break;
            }
        }
    }

  g_atomic_int_dec_and_test (&handler_lock);
}


/*  public functions  */


void
gimp_backtrace_init (void)
{
#ifdef HAVE_LIBBACKTRACE
  backtrace_state = backtrace_create_state (NULL, 0, NULL, NULL);
#endif
}

gboolean
gimp_backtrace_start (void)
{
  g_mutex_lock (&mutex);

  if (n_initializations == 0)
    {
      struct sigaction action = {};

      action.sa_handler = gimp_backtrace_signal_handler;
      action.sa_flags   = SA_RESTART;

      sigemptyset (&action.sa_mask);

      if (sigaction (BACKTRACE_SIGNAL, &action, &orig_action) == 0)
        {
          pid_t *threads;
          gint   n_threads;
          gint   i;

          n_blacklisted_threads = 0;

          threads = g_new (pid_t, MAX_N_THREADS);

          n_threads = gimp_backtrace_enumerate_threads (TRUE,
                                                        threads, MAX_N_THREADS);

          for (i = 0; i < n_threads; i++)
            {
              gchar name[MAX_THREAD_NAME_SIZE];
              gint  j;

              gimp_backtrace_read_thread_name (threads[i],
                                               name, MAX_THREAD_NAME_SIZE);

              for (j = 0; j < G_N_ELEMENTS (blacklisted_thread_names); j++)
                {
                  if (! strcmp (name, blacklisted_thread_names[j]))
                    {
                      blacklisted_threads[n_blacklisted_threads++] = threads[i];
                    }
                }
            }

          g_free (threads);

          initialized = TRUE;
        }
    }

  n_initializations++;

  g_mutex_unlock (&mutex);

  return initialized;
}

void
gimp_backtrace_stop (void)
{
  g_return_if_fail (n_initializations > 0);

  g_mutex_lock (&mutex);

  n_initializations--;

  if (n_initializations == 0 && initialized)
    {
      if (sigaction (BACKTRACE_SIGNAL, &orig_action, NULL) < 0)
        g_warning ("failed to restore original backtrace signal handler");

      initialized = FALSE;
    }

  g_mutex_unlock (&mutex);
}

GimpBacktrace *
gimp_backtrace_new (gboolean include_current_thread)
{
  GimpBacktrace *backtrace;
  pid_t          pid;
  pid_t         *threads;
  gint           n_threads;
  gint64         start_time;
  gint           i;

  if (! initialized)
    return NULL;

  pid = getpid ();

  threads = g_new (pid_t, MAX_N_THREADS);

  n_threads = gimp_backtrace_enumerate_threads (include_current_thread,
                                                threads, MAX_N_THREADS);

  if (n_threads == 0)
    {
      g_free (threads);

      return NULL;
    }

  g_mutex_lock (&mutex);

  backtrace = g_slice_new (GimpBacktrace);

  backtrace->threads   = g_new (GimpBacktraceThread, n_threads);
  backtrace->n_threads = n_threads;

  while (! g_atomic_int_compare_and_exchange (&handler_lock, 0, -1));

  g_atomic_pointer_set (&handler_backtrace,           backtrace);
  g_atomic_int_set     (&handler_n_remaining_threads, n_threads);

  g_atomic_int_set (&handler_lock, 0);

  for (i = 0; i < n_threads; i++)
    {
      GimpBacktraceThread *thread = &backtrace->threads[i];

      thread->tid      = threads[i];
      thread->n_frames = 0;

      gimp_backtrace_read_thread_name (thread->tid,
                                       thread->name, MAX_THREAD_NAME_SIZE);

      thread->state = gimp_backtrace_read_thread_state (thread->tid);

      syscall (SYS_tgkill, pid, threads[i], BACKTRACE_SIGNAL);
    }

  g_free (threads);

  start_time = g_get_monotonic_time ();

  while (g_atomic_int_get (&handler_n_remaining_threads) > 0)
    {
      gint64 time = g_get_monotonic_time ();

      if (time - start_time > MAX_WAIT_TIME)
        break;

      g_usleep (1000);
    }

  while (! g_atomic_int_compare_and_exchange (&handler_lock, 0, -1));

  g_atomic_pointer_set (&handler_backtrace, NULL);

  g_atomic_int_set (&handler_lock, 0);

#if 0
  if (handler_n_remaining_threads > 0)
    {
      gint j = 0;

      for (i = 0; i < n_threads; i++)
        {
          if (backtrace->threads[i].n_frames == 0)
            {
              if (n_blacklisted_threads < MAX_N_THREADS)
                {
                  blacklisted_threads[n_blacklisted_threads++] =
                    backtrace->threads[i].tid;
                }
            }
          else
            {
              if (j < i)
                backtrace->threads[j] = backtrace->threads[i];

              j++;
            }
        }

      n_threads = j;
    }
#endif

  g_mutex_unlock (&mutex);

  if (n_threads == 0)
    {
      gimp_backtrace_free (backtrace);

      return NULL;
    }

  return backtrace;
}

void
gimp_backtrace_free (GimpBacktrace *backtrace)
{
  if (! backtrace)
    return;

  g_free (backtrace->threads);

  g_slice_free (GimpBacktrace, backtrace);
}

gint
gimp_backtrace_get_n_threads (GimpBacktrace *backtrace)
{
  g_return_val_if_fail (backtrace != NULL, 0);

  return backtrace->n_threads;
}

guintptr
gimp_backtrace_get_thread_id (GimpBacktrace *backtrace,
                              gint           thread)
{
  g_return_val_if_fail (backtrace != NULL, 0);
  g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0);

  return backtrace->threads[thread].tid;
}

const gchar *
gimp_backtrace_get_thread_name (GimpBacktrace *backtrace,
                                gint           thread)
{
  g_return_val_if_fail (backtrace != NULL, NULL);
  g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, NULL);

  if (backtrace->threads[thread].name[0])
    return backtrace->threads[thread].name;
  else
    return NULL;
}

gboolean
gimp_backtrace_is_thread_running (GimpBacktrace *backtrace,
                                  gint           thread)
{
  g_return_val_if_fail (backtrace != NULL, FALSE);
  g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, FALSE);

  return backtrace->threads[thread].state == 'R';
}

gint
gimp_backtrace_find_thread_by_id (GimpBacktrace *backtrace,
                                  guintptr       thread_id,
                                  gint           thread_hint)
{
  pid_t tid = thread_id;
  gint  i;

  g_return_val_if_fail (backtrace != NULL, -1);

  if (thread_hint >= 0                    &&
      thread_hint <  backtrace->n_threads &&
      backtrace->threads[thread_hint].tid == tid)
    {
      return thread_hint;
    }

  for (i = 0; i < backtrace->n_threads; i++)
    {
      if (backtrace->threads[i].tid == tid)
        return i;
    }

  return -1;
}

gint
gimp_backtrace_get_n_frames (GimpBacktrace *backtrace,
                             gint           thread)
{
  g_return_val_if_fail (backtrace != NULL, 0);
  g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0);

  return MAX (backtrace->threads[thread].n_frames - N_SKIPPED_FRAMES, 0);
}

guintptr
gimp_backtrace_get_frame_address (GimpBacktrace *backtrace,
                                  gint           thread,
                                  gint           frame)
{
  g_return_val_if_fail (backtrace != NULL, 0);
  g_return_val_if_fail (thread >= 0 && thread < backtrace->n_threads, 0);

  frame = gimp_backtrace_normalize_frame (backtrace, thread, frame);

  g_return_val_if_fail (frame >= N_SKIPPED_FRAMES &&
                        frame <  backtrace->threads[thread].n_frames, 0);

  return backtrace->threads[thread].frames[frame];
}

#ifdef HAVE_LIBBACKTRACE
static void
gimp_backtrace_syminfo_callback (GimpBacktraceAddressInfo *info,
                                 guintptr                  pc,
                                 const gchar              *symname,
                                 guintptr                  symval,
                                 guintptr                  symsize)
{
  if (symname)
    g_strlcpy (info->symbol_name, symname, sizeof (info->symbol_name));

  info->symbol_address = symval;
}

static gint
gimp_backtrace_pcinfo_callback (GimpBacktraceAddressInfo *info,
                                guintptr                  pc,
                                const gchar              *filename,
                                gint                      lineno,
                                const gchar              *function)
{
  if (function)
    g_strlcpy (info->symbol_name, function, sizeof (info->symbol_name));

  if (filename)
    g_strlcpy (info->source_file, filename, sizeof (info->source_file));

  info->source_line = lineno;

  return 0;
}
#endif /* HAVE_LIBBACKTRACE */

gboolean
gimp_backtrace_get_address_info (guintptr                  address,
                                 GimpBacktraceAddressInfo *info)
{
  Dl_info  dl_info;
  gboolean result = FALSE;

  g_return_val_if_fail (info != NULL, FALSE);

  info->object_name[0] = '\0';

  info->symbol_name[0] = '\0';
  info->symbol_address = 0;

  info->source_file[0] = '\0';
  info->source_line    = 0;

  if (dladdr ((gpointer) address, &dl_info))
    {
      if (dl_info.dli_fname)
        {
          g_strlcpy (info->object_name, dl_info.dli_fname,
                     sizeof (info->object_name));
        }

      if (dl_info.dli_sname)
        {
          g_strlcpy (info->symbol_name, dl_info.dli_sname,
                     sizeof (info->symbol_name));
        }

      info->symbol_address = (guintptr) dl_info.dli_saddr;

      result = TRUE;
    }

#ifdef HAVE_LIBBACKTRACE
  if (backtrace_state)
    {
      backtrace_syminfo (
        backtrace_state, address,
        (backtrace_syminfo_callback) gimp_backtrace_syminfo_callback,
        NULL,
        info);

      backtrace_pcinfo (
        backtrace_state, address,
        (backtrace_full_callback) gimp_backtrace_pcinfo_callback,
        NULL,
        info);

      result = TRUE;
    }
#endif /* HAVE_LIBBACKTRACE */

#ifdef HAVE_LIBUNWIND
/* we use libunwind to get the symbol name, when available, even if dladdr() or
 * libbacktrace already found one, since it provides more descriptive names in
 * some cases, and, in particular, full symbol names for C++ lambdas.
 *
 * note that, in some cases, this can result in a discrepancy between the
 * symbol name, and the corresponding source location.
 */

#if 0
  if (! info->symbol_name[0])
#endif
    {
      unw_context_t context = {};
      unw_cursor_t  cursor;
      unw_word_t    offset;

      if (unw_init_local (&cursor, &context)         == 0 &&
          unw_set_reg (&cursor, UNW_REG_IP, address) == 0 &&
          unw_get_proc_name (&cursor,
                             info->symbol_name, sizeof (info->symbol_name),
                             &offset)                == 0)
        {
          info->symbol_address = address - offset;

          result = TRUE;
        }
    }
#endif /* HAVE_LIBUNWIND */

  return result;
}


#endif /* GIMP_BACKTRACE_BACKEND_LINUX */

Messung
CHZG
81929288

¤ Dauer der Verarbeitung: 0.3 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.