Blame malloc/tst-malloc-stats-cancellation.c

Packit 6c4009
/* Bug 22830: malloc_stats fails to re-enable cancellation on exit.
Packit 6c4009
   Copyright (C) 2018 Free Software Foundation.
Packit 6c4009
   Copying and distribution of this file, with or without modification,
Packit 6c4009
   are permitted in any medium without royalty provided the copyright
Packit 6c4009
   notice and this notice are preserved. This file is offered as-is,
Packit 6c4009
   without any warranty.  */
Packit 6c4009
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
Packit 6c4009
#include <pthread.h>
Packit 6c4009
#include <sys/stat.h>
Packit 6c4009
#include <sys/types.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
Packit 6c4009
#include <malloc.h>
Packit 6c4009
Packit 6c4009
static void *
Packit 6c4009
test_threadproc (void *gatep)
Packit 6c4009
{
Packit 6c4009
  /* When we are released from the barrier, there is a cancellation
Packit 6c4009
     request pending for this thread.  N.B. pthread_barrier_wait is
Packit 6c4009
     not itself a cancellation point (oddly enough).  */
Packit 6c4009
  pthread_barrier_wait ((pthread_barrier_t *)gatep);
Packit 6c4009
  malloc_stats ();
Packit 6c4009
  fputs ("this call should trigger cancellation\n", stderr);
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* We cannot replace stderr with a memstream because writes to memstreams
Packit 6c4009
   do not trigger cancellation.  Instead, main swaps out fd 2 to point to
Packit 6c4009
   a pipe, and this thread reads from the pipe and writes to a memstream
Packit 6c4009
   until EOF, then returns the data accumulated in the memstream.  main
Packit 6c4009
   can't do that itself because, when the test thread gets cancelled,
Packit 6c4009
   it doesn't close the pipe.  */
Packit 6c4009
Packit 6c4009
struct buffer_tp_args
Packit 6c4009
{
Packit 6c4009
  int ifd;
Packit 6c4009
  FILE *real_stderr;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
static void *
Packit 6c4009
buffer_threadproc (void *argp)
Packit 6c4009
{
Packit 6c4009
  struct buffer_tp_args *args = argp;
Packit 6c4009
  int ifd = args->ifd;
Packit 6c4009
  char block[BUFSIZ], *p;
Packit 6c4009
  ssize_t nread;
Packit 6c4009
  size_t nwritten;
Packit 6c4009
Packit 6c4009
  char *obuf = 0;
Packit 6c4009
  size_t obufsz = 0;
Packit 6c4009
  FILE *ofp = open_memstream (&obuf, &obufsz);
Packit 6c4009
  if (!ofp)
Packit 6c4009
    {
Packit 6c4009
      fprintf (args->real_stderr,
Packit 6c4009
               "buffer_threadproc: open_memstream: %s\n", strerror (errno));
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  while ((nread = read (ifd, block, BUFSIZ)) > 0)
Packit 6c4009
    {
Packit 6c4009
      p = block;
Packit 6c4009
      do
Packit 6c4009
        {
Packit 6c4009
          nwritten = fwrite_unlocked (p, 1, nread, ofp);
Packit 6c4009
          if (nwritten == 0)
Packit 6c4009
            {
Packit 6c4009
              fprintf (args->real_stderr,
Packit 6c4009
                       "buffer_threadproc: fwrite_unlocked: %s\n",
Packit 6c4009
                       strerror (errno));
Packit 6c4009
              return 0;
Packit 6c4009
            }
Packit 6c4009
          nread -= nwritten;
Packit 6c4009
          p += nwritten;
Packit 6c4009
        }
Packit 6c4009
      while (nread > 0);
Packit 6c4009
    }
Packit 6c4009
  if (nread == -1)
Packit 6c4009
    {
Packit 6c4009
      fprintf (args->real_stderr, "buffer_threadproc: read: %s\n",
Packit 6c4009
               strerror (errno));
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
  close (ifd);
Packit 6c4009
  fclose (ofp);
Packit 6c4009
  return obuf;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
main (void)
Packit 6c4009
{
Packit 6c4009
  int result = 0, err, real_stderr_fd, bufpipe[2];
Packit 6c4009
  pthread_t t_thr, b_thr;
Packit 6c4009
  pthread_barrier_t gate;
Packit 6c4009
  void *rv;
Packit 6c4009
  FILE *real_stderr;
Packit 6c4009
  char *obuf;
Packit 6c4009
  void *obuf_v;
Packit 6c4009
  struct buffer_tp_args b_args;
Packit 6c4009
Packit 6c4009
  real_stderr_fd = dup (2);
Packit 6c4009
  if (real_stderr_fd == -1)
Packit 6c4009
    {
Packit 6c4009
      perror ("dup");
Packit 6c4009
      return 2;
Packit 6c4009
    }
Packit 6c4009
  real_stderr = fdopen(real_stderr_fd, "w");
Packit 6c4009
  if (!real_stderr)
Packit 6c4009
    {
Packit 6c4009
      perror ("fdopen");
Packit 6c4009
      return 2;
Packit 6c4009
    }
Packit 6c4009
  if (setvbuf (real_stderr, 0, _IOLBF, 0))
Packit 6c4009
    {
Packit 6c4009
      perror ("setvbuf(real_stderr)");
Packit 6c4009
      return 2;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (pipe (bufpipe))
Packit 6c4009
    {
Packit 6c4009
      perror ("pipe");
Packit 6c4009
      return 2;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Below this point, nobody other than the test_threadproc should use
Packit 6c4009
     the normal stderr.  */
Packit 6c4009
  if (dup2 (bufpipe[1], 2) == -1)
Packit 6c4009
    {
Packit 6c4009
      fprintf (real_stderr, "dup2: %s\n", strerror (errno));
Packit 6c4009
      return 2;
Packit 6c4009
    }
Packit 6c4009
  close (bufpipe[1]);
Packit 6c4009
Packit 6c4009
  b_args.ifd = bufpipe[0];
Packit 6c4009
  b_args.real_stderr = real_stderr;
Packit 6c4009
  err = pthread_create (&b_thr, 0, buffer_threadproc, &b_args);
Packit 6c4009
  if (err)
Packit 6c4009
    {
Packit 6c4009
      fprintf (real_stderr, "pthread_create(buffer_thr): %s\n",
Packit 6c4009
               strerror (err));
Packit 6c4009
      return 2;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  err = pthread_barrier_init (&gate, 0, 2);
Packit 6c4009
  if (err)
Packit 6c4009
    {
Packit 6c4009
      fprintf (real_stderr, "pthread_barrier_init: %s\n", strerror (err));
Packit 6c4009
      return 2;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  err = pthread_create (&t_thr, 0, test_threadproc, &gate);
Packit 6c4009
  if (err)
Packit 6c4009
    {
Packit 6c4009
      fprintf (real_stderr, "pthread_create(test_thr): %s\n", strerror (err));
Packit 6c4009
      return 2;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  err = pthread_cancel (t_thr);
Packit 6c4009
  if (err)
Packit 6c4009
    {
Packit 6c4009
      fprintf (real_stderr, "pthread_cancel: %s\n", strerror (err));
Packit 6c4009
      return 2;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  pthread_barrier_wait (&gate); /* cannot fail */
Packit 6c4009
Packit 6c4009
  err = pthread_join (t_thr, &rv;;
Packit 6c4009
  if (err)
Packit 6c4009
    {
Packit 6c4009
      fprintf (real_stderr, "pthread_join(test_thr): %s\n", strerror (err));
Packit 6c4009
      return 2;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Closing the normal stderr releases the buffer_threadproc from its
Packit 6c4009
     loop.  */
Packit 6c4009
  fclose (stderr);
Packit 6c4009
  err = pthread_join (b_thr, &obuf_v);
Packit 6c4009
  if (err)
Packit 6c4009
    {
Packit 6c4009
      fprintf (real_stderr, "pthread_join(buffer_thr): %s\n", strerror (err));
Packit 6c4009
      return 2;
Packit 6c4009
    }
Packit 6c4009
  obuf = obuf_v;
Packit 6c4009
  if (obuf == 0)
Packit 6c4009
    return 2; /* error within buffer_threadproc, already reported */
Packit 6c4009
Packit 6c4009
  if (rv != PTHREAD_CANCELED)
Packit 6c4009
    {
Packit 6c4009
      fputs ("FAIL: thread was not cancelled\n", real_stderr);
Packit 6c4009
      result = 1;
Packit 6c4009
    }
Packit 6c4009
  /* obuf should have received all of the text printed by malloc_stats,
Packit 6c4009
     but not the text printed by the final call to fputs.  */
Packit 6c4009
  if (!strstr (obuf, "max mmap bytes"))
Packit 6c4009
    {
Packit 6c4009
      fputs ("FAIL: malloc_stats output incomplete\n", real_stderr);
Packit 6c4009
      result = 1;
Packit 6c4009
    }
Packit 6c4009
  if (strstr (obuf, "this call should trigger cancellation"))
Packit 6c4009
    {
Packit 6c4009
      fputs ("FAIL: fputs produced output\n", real_stderr);
Packit 6c4009
      result = 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (result == 1)
Packit 6c4009
    {
Packit 6c4009
      fputs ("--- output from thread below ---\n", real_stderr);
Packit 6c4009
      fputs (obuf, real_stderr);
Packit 6c4009
    }
Packit 6c4009
  return result;
Packit 6c4009
}