(root)/
glibc-2.38/
malloc/
tst-malloc-stats-cancellation.c
       1  /* Bug 22830: malloc_stats fails to re-enable cancellation on exit.
       2     Copyright (C) 2018 Free Software Foundation.
       3     Copying and distribution of this file, with or without modification,
       4     are permitted in any medium without royalty provided the copyright
       5     notice and this notice are preserved. This file is offered as-is,
       6     without any warranty.  */
       7  
       8  #include <errno.h>
       9  #include <stdio.h>
      10  #include <string.h>
      11  
      12  #include <pthread.h>
      13  #include <sys/stat.h>
      14  #include <sys/types.h>
      15  #include <fcntl.h>
      16  #include <unistd.h>
      17  
      18  #include <malloc.h>
      19  
      20  static void *
      21  test_threadproc (void *gatep)
      22  {
      23    /* When we are released from the barrier, there is a cancellation
      24       request pending for this thread.  N.B. pthread_barrier_wait is
      25       not itself a cancellation point (oddly enough).  */
      26    pthread_barrier_wait ((pthread_barrier_t *)gatep);
      27    malloc_stats ();
      28    fputs ("this call should trigger cancellation\n", stderr);
      29    return 0;
      30  }
      31  
      32  /* We cannot replace stderr with a memstream because writes to memstreams
      33     do not trigger cancellation.  Instead, main swaps out fd 2 to point to
      34     a pipe, and this thread reads from the pipe and writes to a memstream
      35     until EOF, then returns the data accumulated in the memstream.  main
      36     can't do that itself because, when the test thread gets cancelled,
      37     it doesn't close the pipe.  */
      38  
      39  struct buffer_tp_args
      40  {
      41    int ifd;
      42    FILE *real_stderr;
      43  };
      44  
      45  static void *
      46  buffer_threadproc (void *argp)
      47  {
      48    struct buffer_tp_args *args = argp;
      49    int ifd = args->ifd;
      50    char block[BUFSIZ], *p;
      51    ssize_t nread;
      52    size_t nwritten;
      53  
      54    char *obuf = 0;
      55    size_t obufsz = 0;
      56    FILE *ofp = open_memstream (&obuf, &obufsz);
      57    if (!ofp)
      58      {
      59        fprintf (args->real_stderr,
      60                 "buffer_threadproc: open_memstream: %s\n", strerror (errno));
      61        return 0;
      62      }
      63  
      64    while ((nread = read (ifd, block, BUFSIZ)) > 0)
      65      {
      66        p = block;
      67        do
      68          {
      69            nwritten = fwrite_unlocked (p, 1, nread, ofp);
      70            if (nwritten == 0)
      71              {
      72                fprintf (args->real_stderr,
      73                         "buffer_threadproc: fwrite_unlocked: %s\n",
      74                         strerror (errno));
      75                return 0;
      76              }
      77            nread -= nwritten;
      78            p += nwritten;
      79          }
      80        while (nread > 0);
      81      }
      82    if (nread == -1)
      83      {
      84        fprintf (args->real_stderr, "buffer_threadproc: read: %s\n",
      85                 strerror (errno));
      86        return 0;
      87      }
      88    close (ifd);
      89    fclose (ofp);
      90    return obuf;
      91  }
      92  
      93  
      94  static int
      95  do_test (void)
      96  {
      97    int result = 0, err, real_stderr_fd, bufpipe[2];
      98    pthread_t t_thr, b_thr;
      99    pthread_barrier_t gate;
     100    void *rv;
     101    FILE *real_stderr;
     102    char *obuf;
     103    void *obuf_v;
     104    struct buffer_tp_args b_args;
     105  
     106    real_stderr_fd = dup (2);
     107    if (real_stderr_fd == -1)
     108      {
     109        perror ("dup");
     110        return 2;
     111      }
     112    real_stderr = fdopen(real_stderr_fd, "w");
     113    if (!real_stderr)
     114      {
     115        perror ("fdopen");
     116        return 2;
     117      }
     118    if (setvbuf (real_stderr, 0, _IOLBF, 0))
     119      {
     120        perror ("setvbuf(real_stderr)");
     121        return 2;
     122      }
     123  
     124    if (pipe (bufpipe))
     125      {
     126        perror ("pipe");
     127        return 2;
     128      }
     129  
     130    /* Below this point, nobody other than the test_threadproc should use
     131       the normal stderr.  */
     132    if (dup2 (bufpipe[1], 2) == -1)
     133      {
     134        fprintf (real_stderr, "dup2: %s\n", strerror (errno));
     135        return 2;
     136      }
     137    close (bufpipe[1]);
     138  
     139    b_args.ifd = bufpipe[0];
     140    b_args.real_stderr = real_stderr;
     141    err = pthread_create (&b_thr, 0, buffer_threadproc, &b_args);
     142    if (err)
     143      {
     144        fprintf (real_stderr, "pthread_create(buffer_thr): %s\n",
     145                 strerror (err));
     146        return 2;
     147      }
     148  
     149    err = pthread_barrier_init (&gate, 0, 2);
     150    if (err)
     151      {
     152        fprintf (real_stderr, "pthread_barrier_init: %s\n", strerror (err));
     153        return 2;
     154      }
     155  
     156    err = pthread_create (&t_thr, 0, test_threadproc, &gate);
     157    if (err)
     158      {
     159        fprintf (real_stderr, "pthread_create(test_thr): %s\n", strerror (err));
     160        return 2;
     161      }
     162  
     163    err = pthread_cancel (t_thr);
     164    if (err)
     165      {
     166        fprintf (real_stderr, "pthread_cancel: %s\n", strerror (err));
     167        return 2;
     168      }
     169  
     170    pthread_barrier_wait (&gate); /* cannot fail */
     171  
     172    err = pthread_join (t_thr, &rv);
     173    if (err)
     174      {
     175        fprintf (real_stderr, "pthread_join(test_thr): %s\n", strerror (err));
     176        return 2;
     177      }
     178  
     179    /* Closing the normal stderr releases the buffer_threadproc from its
     180       loop.  */
     181    fclose (stderr);
     182    err = pthread_join (b_thr, &obuf_v);
     183    if (err)
     184      {
     185        fprintf (real_stderr, "pthread_join(buffer_thr): %s\n", strerror (err));
     186        return 2;
     187      }
     188    obuf = obuf_v;
     189    if (obuf == 0)
     190      return 2; /* error within buffer_threadproc, already reported */
     191  
     192    if (rv != PTHREAD_CANCELED)
     193      {
     194        fputs ("FAIL: thread was not cancelled\n", real_stderr);
     195        result = 1;
     196      }
     197    /* obuf should have received all of the text printed by malloc_stats,
     198       but not the text printed by the final call to fputs.  */
     199    if (!strstr (obuf, "max mmap bytes"))
     200      {
     201        fputs ("FAIL: malloc_stats output incomplete\n", real_stderr);
     202        result = 1;
     203      }
     204    if (strstr (obuf, "this call should trigger cancellation"))
     205      {
     206        fputs ("FAIL: fputs produced output\n", real_stderr);
     207        result = 1;
     208      }
     209  
     210    if (result == 1)
     211      {
     212        fputs ("--- output from thread below ---\n", real_stderr);
     213        fputs (obuf, real_stderr);
     214      }
     215    return result;
     216  }
     217  
     218  #include <support/test-driver.c>