(root)/
gcc-13.2.0/
libgcc/
libgcov-driver-system.c
       1  /* Routines required for instrumenting a program.  */
       2  /* Compile this one with gcc.  */
       3  /* Copyright (C) 1989-2023 Free Software Foundation, Inc.
       4  
       5  This file is part of GCC.
       6  
       7  GCC is free software; you can redistribute it and/or modify it under
       8  the terms of the GNU General Public License as published by the Free
       9  Software Foundation; either version 3, or (at your option) any later
      10  version.
      11  
      12  GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      13  WARRANTY; without even the implied warranty of MERCHANTABILITY or
      14  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      15  for more details.
      16  
      17  Under Section 7 of GPL version 3, you are granted additional
      18  permissions described in the GCC Runtime Library Exception, version
      19  3.1, as published by the Free Software Foundation.
      20  
      21  You should have received a copy of the GNU General Public License and
      22  a copy of the GCC Runtime Library Exception along with this program;
      23  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
      24  <http://www.gnu.org/licenses/>.  */
      25  
      26  #if !IN_GCOV_TOOL
      27  /* Configured via the GCOV_ERROR_FILE environment variable;
      28     it will either be stderr, or a file of the user's choosing.
      29     Non-static to prevent multiple gcov-aware shared objects from
      30     instantiating their own copies. */
      31  FILE *__gcov_error_file = NULL;
      32  #endif
      33  
      34  /* A utility function to populate the __gcov_error_file pointer.
      35     This should NOT be called outside of the gcov system driver code. */
      36  
      37  static FILE *
      38  get_gcov_error_file (void)
      39  {
      40  #if IN_GCOV_TOOL
      41    return stderr;
      42  #else
      43    if (!__gcov_error_file)
      44      {
      45        const char *gcov_error_filename = getenv ("GCOV_ERROR_FILE");
      46  
      47        if (gcov_error_filename)
      48  	__gcov_error_file = fopen (gcov_error_filename, "a");
      49        if (!__gcov_error_file)
      50  	__gcov_error_file = stderr;
      51      }
      52    return __gcov_error_file;
      53  #endif
      54  }
      55  
      56  /* A utility function for outputting errors.  */
      57  
      58  static int __attribute__((format(printf, 1, 2)))
      59  gcov_error (const char *fmt, ...)
      60  {
      61    int ret;
      62    va_list argp;
      63  
      64    va_start (argp, fmt);
      65    FILE *f = get_gcov_error_file ();
      66    ret = vfprintf (f, fmt, argp);
      67    va_end (argp);
      68  
      69    if (getenv ("GCOV_EXIT_AT_ERROR"))
      70      {
      71        fprintf (f, "profiling:exiting after an error\n");
      72        exit (1);
      73      }
      74  
      75    return ret;
      76  }
      77  
      78  #if !IN_GCOV_TOOL
      79  static void
      80  gcov_error_exit (void)
      81  {
      82    if (__gcov_error_file && __gcov_error_file != stderr)
      83      {
      84        fclose (__gcov_error_file);
      85        __gcov_error_file = NULL;
      86      }
      87  }
      88  #endif
      89  
      90  /* Make sure path component of the given FILENAME exists, create
      91     missing directories. FILENAME must be writable.
      92     Returns zero on success, or -1 if an error occurred.  */
      93  
      94  static int
      95  create_file_directory (char *filename)
      96  {
      97  #if !defined(TARGET_POSIX_IO) && !defined(_WIN32)
      98    (void) filename;
      99    return -1;
     100  #else
     101    char *s;
     102  
     103    s = filename;
     104  
     105    if (HAS_DRIVE_SPEC(s))
     106      s += 2;
     107    if (IS_DIR_SEPARATOR(*s))
     108      ++s;
     109    for (; *s != '\0'; s++)
     110      if (IS_DIR_SEPARATOR(*s))
     111        {
     112          char sep = *s;
     113          *s  = '\0';
     114  
     115          /* Try to make directory if it doesn't already exist.  */
     116          if (access (filename, F_OK) == -1
     117  #ifdef TARGET_POSIX_IO
     118  	    && mkdir (filename, 0777) == -1
     119  #else
     120  #ifdef mkdir
     121  #undef mkdir
     122  #endif
     123              && mkdir (filename) == -1
     124  #endif
     125              /* The directory might have been made by another process.  */
     126              && errno != EEXIST)
     127            {
     128              gcov_error ("profiling:%s:Cannot create directory\n", filename);
     129              *s = sep;
     130              return -1;
     131            };
     132  
     133          *s = sep;
     134        };
     135    return 0;
     136  #endif
     137  }
     138  
     139  /* Replace filename variables in FILENAME.  We currently support expansion:
     140  
     141     %p - process ID
     142     %q{ENV} - value of environment variable ENV
     143     */
     144  
     145  static char *
     146  replace_filename_variables (char *filename)
     147  {
     148    char buffer[16];
     149    char empty[] = "";
     150    for (char *p = filename; *p != '\0'; p++)
     151      {
     152        unsigned length = strlen (filename);
     153        if (*p == '%' && *(p + 1) != '\0')
     154  	{
     155  	  unsigned start = p - filename;
     156  	  p++;
     157  	  char *replacement = NULL;
     158  	  switch (*p)
     159  	    {
     160  	    case 'p':
     161  	      sprintf (buffer, "%d", getpid ());
     162  	      replacement = buffer;
     163  	      p++;
     164  	      break;
     165  	    case 'q':
     166  	      if (*(p + 1) == '{')
     167  		{
     168  		  p += 2;
     169  		  char *e = strchr (p, '}');
     170  		  if (e)
     171  		    {
     172  		      *e = '\0';
     173  		      replacement = getenv (p);
     174  		      if (replacement == NULL)
     175  			replacement = empty;
     176  		      p = e + 1;
     177  		    }
     178  		  else
     179  		    return filename;
     180  		}
     181  	      break;
     182  	    default:
     183  	      return filename;
     184  	    }
     185  
     186  	  /* Concat beginning of the path, replacement and
     187  	     ending of the path.  */
     188  	  unsigned end = length - (p - filename);
     189  	  unsigned repl_length = replacement != NULL ? strlen (replacement) : 0;
     190  
     191  	  char *buffer = (char *)xmalloc (start + end + repl_length + 1);
     192  	  char *buffer_ptr = buffer;
     193  	  buffer_ptr = (char *)memcpy (buffer_ptr, filename, start);
     194  	  buffer_ptr += start;
     195  	  if (replacement != NULL)
     196  	    buffer_ptr = (char *)memcpy (buffer_ptr, replacement, repl_length);
     197  	  buffer_ptr += repl_length;
     198  	  buffer_ptr = (char *)memcpy (buffer_ptr, p, end);
     199  	  buffer_ptr += end;
     200  	  *buffer_ptr = '\0';
     201  
     202  	  free (filename);
     203  	  filename = buffer;
     204  	  p = buffer + start + repl_length;
     205  	}
     206      }
     207  
     208    return filename;
     209  }
     210  
     211  static void
     212  allocate_filename_struct (struct gcov_filename *gf)
     213  {
     214    const char *gcov_prefix;
     215    size_t prefix_length;
     216    int strip = 0;
     217    gf->filename = NULL;
     218  
     219    {
     220      /* Check if the level of dirs to strip off specified. */
     221      char *tmp = getenv("GCOV_PREFIX_STRIP");
     222      if (tmp)
     223        {
     224          strip = atoi (tmp);
     225          /* Do not consider negative values. */
     226          if (strip < 0)
     227            strip = 0;
     228        }
     229    }
     230    gf->strip = strip;
     231  
     232    /* Get file name relocation prefix.  Non-absolute values are ignored. */
     233    gcov_prefix = getenv("GCOV_PREFIX");
     234    prefix_length = gcov_prefix ? strlen (gcov_prefix) : 0;
     235    
     236    /* Remove an unnecessary trailing '/' */
     237    if (prefix_length && IS_DIR_SEPARATOR (gcov_prefix[prefix_length - 1]))
     238      prefix_length--;
     239  
     240    /* If no prefix was specified and a prefix stip, then we assume
     241       relative.  */
     242    if (!prefix_length && gf->strip)
     243      {
     244        gcov_prefix = ".";
     245        prefix_length = 1;
     246      }
     247  
     248    /* Allocate and initialize the filename scratch space.  */
     249    if (prefix_length)
     250      {
     251        gf->prefix = (char *) xmalloc (prefix_length + 1);
     252        char *p = (char *) memcpy (gf->prefix, gcov_prefix, prefix_length);
     253        *(p + prefix_length) = '\0';
     254      }
     255    else
     256      gf->prefix = NULL;
     257  }
     258  
     259  /* Open a gcda file specified by GI_FILENAME.
     260     Return -1 on error.  Return 0 on success.  */
     261  
     262  static int
     263  gcov_exit_open_gcda_file (struct gcov_info *gi_ptr,
     264  			  struct gcov_filename *gf,
     265  			  int mode)
     266  {
     267    int append_slash = 0;
     268    const char *fname = gi_ptr->filename;
     269  
     270    /* Build relocated filename, stripping off leading
     271       directories from the initial filename if requested. */
     272    if (gf->strip > 0)
     273      {
     274        const char *probe = fname;
     275        int level;
     276  
     277        /* Remove a leading separator, without counting it.  */
     278        if (IS_DIR_SEPARATOR (*probe))
     279  	probe++;
     280  
     281        /* Skip selected directory levels.  If we fall off the end, we
     282  	 keep the final part.  */
     283        for (level = gf->strip; *probe && level; probe++)
     284          if (IS_DIR_SEPARATOR (*probe))
     285            {
     286              fname = probe;
     287              level--;
     288            }
     289      }
     290  
     291    /* Update complete filename with stripped original. */
     292    if (gf->prefix)
     293      {
     294        /* Avoid to add multiple drive letters into combined path.  */
     295        if (HAS_DRIVE_SPEC(fname))
     296  	fname += 2;
     297  
     298        if (!IS_DIR_SEPARATOR (*fname))
     299  	append_slash = 1;
     300      }
     301  
     302    size_t prefix_length = gf->prefix ? strlen (gf->prefix) : 0;
     303    gf->filename = (char *) xmalloc (prefix_length + strlen (fname) + 2);
     304    *gf->filename = '\0';
     305    if (prefix_length)
     306      strcat (gf->filename, gf->prefix);
     307    if (append_slash)
     308      *gf->filename++ = '/';
     309    strcat (gf->filename, fname);
     310  
     311    gf->filename = replace_filename_variables (gf->filename);
     312  
     313    if (!gcov_open (gf->filename, mode))
     314      {
     315        /* Open failed likely due to missed directory.
     316           Create directory and retry to open file. */
     317        if (create_file_directory (gf->filename))
     318          {
     319            fprintf (stderr, "profiling:%s:Skip\n", gf->filename);
     320            return -1;
     321          }
     322        if (!gcov_open (gf->filename, mode))
     323          {
     324            fprintf (stderr, "profiling:%s:Cannot open\n", gf->filename);
     325            return -1;
     326          }
     327      }
     328  
     329    return 0;
     330  }