]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
eu-stacktrace WIP: rearrange the code into sections
authorSerhei Makarov <serhei@serhei.io>
Wed, 21 Aug 2024 14:16:34 +0000 (10:16 -0400)
committerSerhei Makarov <serhei@serhei.io>
Wed, 21 Aug 2024 14:17:26 +0000 (10:17 -0400)
Put things in a more logical order as previous incremental
changes led to slightly arbitrary sequence of declarations.

src/stacktrace.c

index 3ca9eaf53d265a95bc6cf8e7d8689fb27a4ab959..6fb9f78bbae42adbfa0c7d661cc8c79565bf01ed 100644 (file)
 #include <string.h>
 #include <fcntl.h>
 #include <signal.h>
+
+#include <system.h>
+
+/*************************************
+ * Includes: libdwfl data structures *
+ *************************************/
+
 /* #include ELFUTILS_HEADER(dwfl) */
 #include "../libdwfl/libdwflP.h"
 /* XXX: Private header needed for sysprof_find_procfile, sysprof_init_dwfl, XXX LIBDWFL_TRACKS_UNWOUND_SOURCE. */
@@ -100,7 +107,9 @@ unwound_source_str (Dwfl_Unwound_Source unwound_source)
 }
 #endif
 
-#include <system.h>
+/*************************************
+ * Includes: sysprof data structures *
+ *************************************/
 
 #if HAVE_SYSPROF_6_HEADERS
 #include <sysprof-6/sysprof-capture-types.h>
@@ -142,11 +151,16 @@ typedef struct
 } SysprofCaptureUserRegs
 SYSPROF_ALIGNED_END(1);
 
-static int maxframes = 256;
-
 #endif /* SYSPROF_CAPTURE_FRAME_STACK_USER */
+
 #endif /* HAVE_SYSPROF_HEADERS */
 
+/**************************
+ * Global data structures *
+ **************************/
+
+static int maxframes = 256;
+
 static char *input_path = NULL;
 static int input_fd = -1;
 static char *output_path = NULL;
@@ -158,14 +172,14 @@ static int signal_count = 0;
 #define MODE_NONE 0x0
 #define MODE_PASSTHRU 0x1
 #define MODE_NAIVE 0x2
-#define MODE_CACHING 0x3
+/* TODO: #define MODE_CACHING 0x3 */
 static int processing_mode = MODE_NAIVE;
 
 #define FORMAT_OPTS "sysprof"
 #define FORMAT_PERF 0x1
 #define FORMAT_SYSPROF 0x2
 static int input_format;
-static int output_format = FORMAT_SYSPROF; /* TODO: add to cmdline args? */
+static int output_format = FORMAT_SYSPROF; /* TODO: add to cmdline args */
 
 /* non-printable argp options.  */
 #define OPT_DEBUG      0x100
@@ -194,16 +208,20 @@ static bool show_summary = true; /* TODO: disable by default in release version
 #define EXIT_BAD    2
 #define EXIT_USAGE 64
 
-/* Sysprof format support.
-   TODO: Could split into a separate file or even a library. */
-
-#if HAVE_SYSPROF_HEADERS
+/**************************
+ * Sysprof format support *
+ **************************/
 
 /* XXX based on sysprof src/libsysprof-capture/sysprof-capture-reader.c
 
    Note: BSD license attribution at the top of the file applies to this
-   segment. If moving the code to a separate library, feel free to
-   move the notice together with it. */
+   segment. Could split into a separate file or even a library,
+   in which case the attribution notice will move along with it. */
+
+/* TODO: elfutils (internal) libraries use libNN_set_errno and _E_WHATEVER;
+   this code sets errno variable directly and uses standard EWHATEVER. */
+
+#if HAVE_SYSPROF_HEADERS
 
 /* A complete passthrough can be implemented based on the following 7 functions:
  - sysprof_reader_begin/sysprof_reader_end :: sysprof_capture_reader_new_from_fd
@@ -254,7 +272,6 @@ sysprof_reader_begin (int fd)
 
   assert (fd > -1);
 
-  /* TODO elfutils style: libraries use __lib??_seterrno and ??_E_ENOMEM. */
   reader = malloc (sizeof (SysprofReader));
   if (reader == NULL)
     {
@@ -450,147 +467,9 @@ sysprof_reader_getframes (SysprofReader *reader,
 
 #endif /* HAVE_SYSPROF_HEADERS */
 
-/* Required to match our signal handling with that of a sysprof parent process. */
-static void sigint_handler (int /* signo */)
-{
-  if (signal_count >= 2)
-    {
-      exit(1);
-    }
-
-  if (signal_count == 0)
-    {
-      fprintf (stderr, "%s\n", "Waiting for input to finish. Press twice more ^C to force exit.");
-    }
-
-  signal_count ++;
-}
-
-/* Main program. */
-
-static error_t
-parse_opt (int key, char *arg __attribute__ ((unused)),
-          struct argp_state *state)
-{
-  switch (key)
-    {
-    case 'i':
-      input_path = arg;
-      break;
-
-    case 'o':
-      output_path = arg;
-      break;
-
-    case 'm':
-      if (strcmp (arg, "none") == 0)
-       {
-         processing_mode = MODE_NONE;
-       }
-      else if (strcmp (arg, "passthru") == 0)
-       {
-         processing_mode = MODE_PASSTHRU;
-       }
-      else if (strcmp (arg, "naive") == 0)
-       {
-         processing_mode = MODE_NAIVE;
-       }
-      else
-       {
-         argp_error (state, N_("Unsupported -m '%s', should be " MODE_OPTS "."), arg);
-       }
-      break;
-
-    case 'f':
-      if (strcmp (arg, "sysprof") == 0)
-       {
-         input_format = FORMAT_SYSPROF;
-       }
-      else
-       {
-         argp_error (state, N_("Unsupported -f '%s', should be " FORMAT_OPTS "."), arg);
-       }
-      break;
-
-    case OPT_DEBUG:
-      show_memory_reads = false;
-      show_frames = true;
-      FALLTHROUGH;
-    case 'v':
-      show_samples = true;
-      show_failures = true;
-      show_summary = true;
-      break;
-
-    case ARGP_KEY_END:
-      if (input_path == NULL)
-       input_path = "-"; /* default to stdin */
-
-      if (output_path == NULL)
-       output_path = "-"; /* default to stdout */
-
-      if (processing_mode == 0)
-       processing_mode = MODE_PASSTHRU;
-      /* TODO: Change the default to MODE_NAIVE once unwinding works reliably. */
-
-      if (input_format == 0)
-       input_format = FORMAT_SYSPROF;
-      break;
-
-    default:
-      return ARGP_ERR_UNKNOWN;
-    }
-  return 0;
-}
-
-#if HAVE_SYSPROF_HEADERS
-
-int
-sysprof_none_cb (SysprofCaptureFrame *frame __attribute__ ((unused)),
-                void *arg __attribute__ ((unused)))
-{
-  return SYSPROF_CB_OK;
-}
-
-struct sysprof_passthru_info
-{
-  int output_fd;
-  SysprofReader *reader;
-  int pos; /* for diagnostic purposes */
-};
-
-int
-sysprof_passthru_cb (SysprofCaptureFrame *frame, void *arg)
-{
-  struct sysprof_passthru_info *spi = (struct sysprof_passthru_info *)arg;
-  sysprof_reader_bswap_frame (spi->reader, frame); /* reverse the earlier bswap */
-  ssize_t n_write = write (spi->output_fd, frame, frame->len);
-  spi->pos += frame->len;
-  assert ((spi->pos % SYSPROF_CAPTURE_ALIGN) == 0);
-  if (n_write < 0)
-    error (EXIT_BAD, errno, N_("Write error to file or FIFO '%s'"), output_path);
-  return SYSPROF_CB_OK;
-}
-
-#define UNWIND_ADDR_INCREMENT 512
-struct sysprof_unwind_info
-{
-  int output_fd;
-  SysprofReader *reader;
-  int pos; /* for diagnostic purposes */
-  int n_addrs;
-  int max_addrs; /* for diagnostic purposes */
-  Dwarf_Addr last_base; /* for diagnostic purposes */
-  Dwarf_Addr last_sp; /* for diagnostic purposes */
-#ifdef DEBUG_MODULES
-  Dwfl *last_dwfl; /* for diagnostic purposes */
-#endif
-#ifdef LIBDWFL_TRACKS_UNWOUND_SOURCE
-  int last_pid; /* for diagnostic purposes, to provide access to dwfltab */
-#endif
-  Dwarf_Addr *addrs; /* allocate blocks of UNWIND_ADDR_INCREMENT */
-  void *outbuf;
-};
+/*******************************************
+ * Memory read interface for stack samples *
+ *******************************************/
 
 struct __sample_arg
 {
@@ -603,10 +482,10 @@ struct __sample_arg
   Dwarf_Addr *regs;
 };
 
-/* The next few functions Imitate the corefile interface for a single
+/* The next few functions imitate the corefile interface for a single
    stack sample, with very restricted access to registers and memory. */
 
-/* Just yields the single thread id matching the sample. */
+/* Just yield the single thread id matching the sample. */
 static pid_t
 sample_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
                    void **thread_argp)
@@ -621,7 +500,7 @@ sample_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
     return 0;
 }
 
-/* Just checks that the thread id matches the sample. */
+/* Just check that the thread id matches the sample. */
 static bool
 sample_getthread (Dwfl *dwfl __attribute__ ((unused)), pid_t tid,
                  void *dwfl_arg, void **thread_argp)
@@ -645,7 +524,7 @@ sample_memory_read (Dwfl *dwfl __attribute__ ((unused)), Dwarf_Addr addr, Dwarf_
            sample_arg->base_addr, addr - sample_arg->base_addr, sample_arg->size);
   /* Imitate read_cached_memory() with the stack sample data as the cache. */
   if (addr < sample_arg->base_addr || addr - sample_arg->base_addr >= sample_arg->size)
-    return false;
+    return false; /* TODO: also read from Elf files we happen to be aware of */
   uint8_t *d = &sample_arg->data[addr - sample_arg->base_addr];
   if ((((uintptr_t) d) & (sizeof (unsigned long) - 1)) == 0)
     *result = *(unsigned long *)d;
@@ -679,14 +558,210 @@ sample_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
   free (sample_arg);
 }
 
-static const Dwfl_Thread_Callbacks sample_thread_callbacks =
+static const Dwfl_Thread_Callbacks sample_thread_callbacks =
+{
+  sample_next_thread,
+  sample_getthread,
+  sample_memory_read,
+  sample_set_initial_registers,
+  sample_detach,
+  NULL, /* sample_thread_detach */
+};
+
+/****************************************************
+ * Dwfl and statistics table for multiple processes *
+ ****************************************************/
+
+/* TODO: This echoes lib/dynamicsizehash.* with some modifications. */
+typedef struct
+{
+  bool used;
+  pid_t pid;
+  Dwfl *dwfl;
+  char *comm;
+  int max_frames; /* for diagnostic purposes */
+  int total_samples; /* for diagnostic purposes */
+  int lost_samples; /* for diagnostic purposes */
+  int shown_error; /* already shown an error for this pid? TODO */
+#ifdef LIBDWFL_TRACKS_UNWOUND_SOURCE
+  Dwfl_Unwound_Source last_unwound; /* track CFI source with enum above, for diagnostic purposes */
+  Dwfl_Unwound_Source worst_unwound; /* track CFI source with enum above, for diagnostic purposes */
+#endif
+} dwfltab_ent;
+
+typedef struct
+{
+  ssize_t size;
+  ssize_t filled;
+  dwfltab_ent *table;
+} dwfltab;
+
+/* TODO: Store in sui, update below functions. */
+/* XXX initial size must be a prime */
+#define DWFLTAB_DEFAULT_SIZE 1021
+dwfltab default_table;
+
+/* XXX based on lib/dynamicsizehash.* *_init */
+void dwfltab_init(void)
+{
+  dwfltab *htab = &default_table;
+  htab->size = DWFLTAB_DEFAULT_SIZE;
+  htab->filled = 0;
+  htab->table = calloc ((htab->size + 1), sizeof(htab->table[0]));
+}
+
+/* XXX based on lib/dynamicsizehash.* *_find */
+dwfltab_ent *dwfltab_find(pid_t pid)
+{
+  dwfltab *htab = &default_table;
+  ssize_t idx = 1 + (htab->size > (ssize_t)pid ? (ssize_t)pid : (ssize_t)pid % htab->size);
+
+  if (!htab->table[idx].used)
+    goto found;
+  if (htab->table[idx].pid == pid)
+    goto found;
+
+  int64_t hash = 1 + pid % (htab->size - 2);
+  do
+    {
+      if (idx <= hash)
+       idx = htab->size + idx - hash;
+      else
+       idx -= hash;
+
+      if (!htab->table[idx].used)
+       goto found;
+      if (htab->table[idx].pid == pid)
+       goto found;
+    }
+  while (true);
+
+ found:
+  if (htab->table[idx].pid == 0)
+    {
+      /* TODO: Implement resizing or LRU eviction? */
+      if (100 * htab->filled > 90 * htab->size)
+       return NULL;
+      htab->table[idx].used = true;
+      htab->table[idx].pid = pid;
+      htab->filled += 1;
+    }
+  return &htab->table[idx];
+}
+
+Dwfl *
+pid_find_dwfl (pid_t pid)
+{
+  dwfltab_ent *entry = dwfltab_find(pid);
+  if (entry == NULL)
+    return NULL;
+  return entry->dwfl;
+}
+
+char *
+pid_find_comm (pid_t pid)
+{
+  dwfltab_ent *entry = dwfltab_find(pid);
+  if (entry == NULL)
+    return "<unknown>";
+  if (entry->comm != NULL)
+    return entry->comm;
+  char name[64];
+  int i = snprintf (name, sizeof(name), "/proc/%ld/comm", (long) pid);
+  FILE *procfile = fopen(name, "r");
+  if (procfile == NULL)
+    goto fail;
+  size_t linelen = 0;
+  i = getline(&entry->comm, &linelen, procfile);
+  if (i < 0)
+    {
+      free(entry->comm);
+      goto fail;
+    }
+  for (i = linelen - 1; i > 0; i--)
+    if (entry->comm[i] == '\n')
+       entry->comm[i] = '\0';
+  fclose(procfile);
+  goto done;
+ fail:
+  entry->comm = (char *)malloc(16);
+  snprintf (entry->comm, 16, "<unknown>");
+ done:
+  return entry->comm;
+}
+
+/* Cache dwfl structs in a basic hash table. */
+void
+pid_store_dwfl (pid_t pid, Dwfl *dwfl)
+{
+  dwfltab_ent *entry = dwfltab_find(pid);
+  if (entry == NULL)
+    return;
+  entry->pid = pid;
+  entry->dwfl = dwfl;
+  pid_find_comm(pid);
+  return;
+}
+
+/*****************************************************
+ * Sysprof backend: basic none/passthrough callbacks *
+ *****************************************************/
+
+#if HAVE_SYSPROF_HEADERS
+
+int
+sysprof_none_cb (SysprofCaptureFrame *frame __attribute__ ((unused)),
+                void *arg __attribute__ ((unused)))
+{
+  return SYSPROF_CB_OK;
+}
+
+struct sysprof_passthru_info
+{
+  int output_fd;
+  SysprofReader *reader;
+  int pos; /* for diagnostic purposes */
+};
+
+int
+sysprof_passthru_cb (SysprofCaptureFrame *frame, void *arg)
+{
+  struct sysprof_passthru_info *spi = (struct sysprof_passthru_info *)arg;
+  sysprof_reader_bswap_frame (spi->reader, frame); /* reverse the earlier bswap */
+  ssize_t n_write = write (spi->output_fd, frame, frame->len);
+  spi->pos += frame->len;
+  assert ((spi->pos % SYSPROF_CAPTURE_ALIGN) == 0);
+  if (n_write < 0)
+    error (EXIT_BAD, errno, N_("Write error to file or FIFO '%s'"), output_path);
+  return SYSPROF_CB_OK;
+}
+
+#endif /* HAVE_SYSPROF_HEADERS */
+
+/****************************************
+ * Sysprof backend: unwinding callbacks *
+ ****************************************/
+
+#if HAVE_SYSPROF_HEADERS
+
+#define UNWIND_ADDR_INCREMENT 512
+struct sysprof_unwind_info
 {
-  sample_next_thread,
-  sample_getthread,
-  sample_memory_read,
-  sample_set_initial_registers,
-  sample_detach,
-  NULL, /* sample_thread_detach */
+  int output_fd;
+  SysprofReader *reader;
+  int pos; /* for diagnostic purposes */
+  int n_addrs;
+  int max_addrs; /* for diagnostic purposes */
+  Dwarf_Addr last_base; /* for diagnostic purposes */
+  Dwarf_Addr last_sp; /* for diagnostic purposes */
+#ifdef DEBUG_MODULES
+  Dwfl *last_dwfl; /* for diagnostic purposes */
+#endif
+#ifdef LIBDWFL_TRACKS_UNWOUND_SOURCE
+  int last_pid; /* for diagnostic purposes, to provide access to dwfltab */
+#endif
+  Dwarf_Addr *addrs; /* allocate blocks of UNWIND_ADDR_INCREMENT */
+  void *outbuf;
 };
 
 #ifdef FIND_DEBUGINFO
@@ -815,137 +890,6 @@ sysprof_find_procfile (Dwfl *dwfl, pid_t *pid, Elf **elf, int *elf_fd)
   return 0;
 }
 
-/* TODO: This echoes lib/dynamicsizehash.* with some modifications. */
-typedef struct
-{
-  bool used;
-  pid_t pid;
-  Dwfl *dwfl;
-  char *comm;
-  int max_frames; /* for diagnostic purposes */
-  int total_samples; /* for diagnostic purposes */
-  int lost_samples; /* for diagnostic purposes */
-  int shown_error; /* already shown an error for this pid? TODO */
-#ifdef LIBDWFL_TRACKS_UNWOUND_SOURCE
-  Dwfl_Unwound_Source last_unwound; /* track CFI source with enum above, for diagnostic purposes */
-  Dwfl_Unwound_Source worst_unwound; /* track CFI source with enum above, for diagnostic purposes */
-#endif
-} dwfltab_ent;
-
-typedef struct
-{
-  ssize_t size;
-  ssize_t filled;
-  dwfltab_ent *table;
-} dwfltab;
-
-/* TODO: Store in sui, update below functions. */
-/* XXX initial size must be a prime */
-#define DWFLTAB_DEFAULT_SIZE 1021
-dwfltab default_table;
-
-/* XXX based on lib/dynamicsizehash.* *_init */
-void dwfltab_init(void)
-{
-  dwfltab *htab = &default_table;
-  htab->size = DWFLTAB_DEFAULT_SIZE;
-  htab->filled = 0;
-  htab->table = calloc ((htab->size + 1), sizeof(htab->table[0]));
-}
-
-/* XXX based on lib/dynamicsizehash.* *_find */
-dwfltab_ent *dwfltab_find(pid_t pid)
-{
-  dwfltab *htab = &default_table;
-  ssize_t idx = 1 + (htab->size > (ssize_t)pid ? (ssize_t)pid : (ssize_t)pid % htab->size);
-
-  if (!htab->table[idx].used)
-    goto found;
-  if (htab->table[idx].pid == pid)
-    goto found;
-
-  int64_t hash = 1 + pid % (htab->size - 2);
-  do
-    {
-      if (idx <= hash)
-       idx = htab->size + idx - hash;
-      else
-       idx -= hash;
-
-      if (!htab->table[idx].used)
-       goto found;
-      if (htab->table[idx].pid == pid)
-       goto found;
-    }
-  while (true);
-
- found:
-  if (htab->table[idx].pid == 0)
-    {
-      /* TODO: Implement resizing or LRU eviction? */
-      if (100 * htab->filled > 90 * htab->size)
-       return NULL;
-      htab->table[idx].used = true;
-      htab->table[idx].pid = pid;
-      htab->filled += 1;
-    }
-  return &htab->table[idx];
-}
-
-Dwfl *
-pid_find_dwfl (pid_t pid)
-{
-  dwfltab_ent *entry = dwfltab_find(pid);
-  if (entry == NULL)
-    return NULL;
-  return entry->dwfl;
-}
-
-char *
-pid_find_comm (pid_t pid)
-{
-  dwfltab_ent *entry = dwfltab_find(pid);
-  if (entry == NULL)
-    return "<unknown>";
-  if (entry->comm != NULL)
-    return entry->comm;
-  char name[64];
-  int i = snprintf (name, sizeof(name), "/proc/%ld/comm", (long) pid);
-  FILE *procfile = fopen(name, "r");
-  if (procfile == NULL)
-    goto fail;
-  size_t linelen = 0;
-  i = getline(&entry->comm, &linelen, procfile);
-  if (i < 0)
-    {
-      free(entry->comm);
-      goto fail;
-    }
-  for (i = linelen - 1; i > 0; i--)
-    if (entry->comm[i] == '\n')
-       entry->comm[i] = '\0';
-  fclose(procfile);
-  goto done;
- fail:
-  entry->comm = (char *)malloc(16);
-  snprintf (entry->comm, 16, "<unknown>");
- done:
-  return entry->comm;
-}
-
-/* Cache dwfl structs in a basic hash table. */
-void
-pid_store_dwfl (pid_t pid, Dwfl *dwfl)
-{
-  dwfltab_ent *entry = dwfltab_find(pid);
-  if (entry == NULL)
-    return;
-  entry->pid = pid;
-  entry->dwfl = dwfl;
-  pid_find_comm(pid);
-  return;
-}
-
 Dwfl *
 sysprof_init_dwfl (struct sysprof_unwind_info *sui,
                   SysprofCaptureStackUser *ev,
@@ -1236,8 +1180,103 @@ sysprof_unwind_cb (SysprofCaptureFrame *frame, void *arg)
     error (EXIT_BAD, errno, N_("Write error to file or FIFO '%s'"), output_path);
   return SYSPROF_CB_OK;
 }
+
 #endif /* HAVE_SYSPROF_HEADERS */
 
+/****************
+ * Main program *
+ ****************/
+
+/* Required to match our signal handling with that of a sysprof parent process. */
+static void sigint_handler (int /* signo */)
+{
+  if (signal_count >= 2)
+    {
+      exit(1);
+    }
+
+  if (signal_count == 0)
+    {
+      fprintf (stderr, "%s\n", "Waiting for input to finish. Press twice more ^C to force exit.");
+    }
+
+  signal_count ++;
+}
+
+static error_t
+parse_opt (int key, char *arg __attribute__ ((unused)),
+          struct argp_state *state)
+{
+  switch (key)
+    {
+    case 'i':
+      input_path = arg;
+      break;
+
+    case 'o':
+      output_path = arg;
+      break;
+
+    case 'm':
+      if (strcmp (arg, "none") == 0)
+       {
+         processing_mode = MODE_NONE;
+       }
+      else if (strcmp (arg, "passthru") == 0)
+       {
+         processing_mode = MODE_PASSTHRU;
+       }
+      else if (strcmp (arg, "naive") == 0)
+       {
+         processing_mode = MODE_NAIVE;
+       }
+      else
+       {
+         argp_error (state, N_("Unsupported -m '%s', should be " MODE_OPTS "."), arg);
+       }
+      break;
+
+    case 'f':
+      if (strcmp (arg, "sysprof") == 0)
+       {
+         input_format = FORMAT_SYSPROF;
+       }
+      else
+       {
+         argp_error (state, N_("Unsupported -f '%s', should be " FORMAT_OPTS "."), arg);
+       }
+      break;
+
+    case OPT_DEBUG:
+      show_memory_reads = false;
+      show_frames = true;
+      FALLTHROUGH;
+    case 'v':
+      show_samples = true;
+      show_failures = true;
+      show_summary = true;
+      break;
+
+    case ARGP_KEY_END:
+      if (input_path == NULL)
+       input_path = "-"; /* default to stdin */
+
+      if (output_path == NULL)
+       output_path = "-"; /* default to stdout */
+
+      if (processing_mode == 0)
+       processing_mode = MODE_NAIVE;
+
+      if (input_format == 0)
+       input_format = FORMAT_SYSPROF;
+      break;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+  return 0;
+}
+
 int
 main (int argc, char **argv)
 {
@@ -1301,8 +1340,14 @@ Utility is a work-in-progress, see README.eu-stacktrace in the source branch.")
     error (EXIT_BAD, errno, N_("Cannot set signal handler for SIGINT"));
 
 #if !(HAVE_SYSPROF_HEADERS)
-  /* TODO: Should hide corresponding command line options when this is the case. */
+  /* TODO: Should hide corresponding command line options when this is the case? */
   error (EXIT_BAD, 0, N_("Sysprof support is not available in this version."));
+
+  /* XXX: The following are not specific to the Sysprof backend;
+     avoid unused-variable warnings while it is the only backend. */
+  (void)sample_thread_callbacks;
+  (void)output_format;
+  (void)maxframes;
 #else
   /* TODO: For now, code the processing loop for sysprof only; generalize later. */
   assert (input_format == FORMAT_SYSPROF);