mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			331 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			331 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C
		
	
	
	
| /* Routines required for instrumenting a program.  */
 | |
| /* Compile this one with gcc.  */
 | |
| /* Copyright (C) 1989-2019 Free Software Foundation, Inc.
 | |
| 
 | |
| This file is part of GCC.
 | |
| 
 | |
| GCC 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, or (at your option) any later
 | |
| version.
 | |
| 
 | |
| GCC 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.
 | |
| 
 | |
| Under Section 7 of GPL version 3, you are granted additional
 | |
| permissions described in the GCC Runtime Library Exception, version
 | |
| 3.1, as published by the Free Software Foundation.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License and
 | |
| a copy of the GCC Runtime Library Exception along with this program;
 | |
| see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 | |
| <http://www.gnu.org/licenses/>.  */
 | |
| 
 | |
| #if !IN_GCOV_TOOL
 | |
| /* Configured via the GCOV_ERROR_FILE environment variable;
 | |
|    it will either be stderr, or a file of the user's choosing.
 | |
|    Non-static to prevent multiple gcov-aware shared objects from
 | |
|    instantiating their own copies. */
 | |
| FILE *__gcov_error_file = NULL;
 | |
| #endif
 | |
| 
 | |
| /* A utility function to populate the __gcov_error_file pointer.
 | |
|    This should NOT be called outside of the gcov system driver code. */
 | |
| 
 | |
| static FILE *
 | |
| get_gcov_error_file (void)
 | |
| {
 | |
| #if IN_GCOV_TOOL
 | |
|   return stderr;
 | |
| #else
 | |
|   if (!__gcov_error_file)
 | |
|     {
 | |
|       const char *gcov_error_filename = getenv ("GCOV_ERROR_FILE");
 | |
| 
 | |
|       if (gcov_error_filename)
 | |
| 	__gcov_error_file = fopen (gcov_error_filename, "a");
 | |
|       if (!__gcov_error_file)
 | |
| 	__gcov_error_file = stderr;
 | |
|     }
 | |
|   return __gcov_error_file;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /* A utility function for outputting errors.  */
 | |
| 
 | |
| static int __attribute__((format(printf, 1, 2)))
 | |
| gcov_error (const char *fmt, ...)
 | |
| {
 | |
|   int ret;
 | |
|   va_list argp;
 | |
| 
 | |
|   va_start (argp, fmt);
 | |
|   FILE *f = get_gcov_error_file ();
 | |
|   ret = vfprintf (f, fmt, argp);
 | |
|   va_end (argp);
 | |
| 
 | |
|   if (getenv ("GCOV_EXIT_AT_ERROR"))
 | |
|     {
 | |
|       fprintf (f, "profiling:exiting after an error\n");
 | |
|       exit (1);
 | |
|     }
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| #if !IN_GCOV_TOOL
 | |
| static void
 | |
| gcov_error_exit (void)
 | |
| {
 | |
|   if (__gcov_error_file && __gcov_error_file != stderr)
 | |
|     {
 | |
|       fclose (__gcov_error_file);
 | |
|       __gcov_error_file = NULL;
 | |
|     }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /* Make sure path component of the given FILENAME exists, create
 | |
|    missing directories. FILENAME must be writable.
 | |
|    Returns zero on success, or -1 if an error occurred.  */
 | |
| 
 | |
| static int
 | |
| create_file_directory (char *filename)
 | |
| {
 | |
| #if !defined(TARGET_POSIX_IO) && !defined(_WIN32)
 | |
|   (void) filename;
 | |
|   return -1;
 | |
| #else
 | |
|   char *s;
 | |
| 
 | |
|   s = filename;
 | |
| 
 | |
|   if (HAS_DRIVE_SPEC(s))
 | |
|     s += 2;
 | |
|   if (IS_DIR_SEPARATOR(*s))
 | |
|     ++s;
 | |
|   for (; *s != '\0'; s++)
 | |
|     if (IS_DIR_SEPARATOR(*s))
 | |
|       {
 | |
|         char sep = *s;
 | |
|         *s  = '\0';
 | |
| 
 | |
|         /* Try to make directory if it doesn't already exist.  */
 | |
|         if (access (filename, F_OK) == -1
 | |
| #ifdef TARGET_POSIX_IO
 | |
|             && mkdir (filename, 0755) == -1
 | |
| #else
 | |
| #ifdef mkdir
 | |
| #undef mkdir
 | |
| #endif
 | |
|             && mkdir (filename) == -1
 | |
| #endif
 | |
|             /* The directory might have been made by another process.  */
 | |
|             && errno != EEXIST)
 | |
|           {
 | |
|             gcov_error ("profiling:%s:Cannot create directory\n", filename);
 | |
|             *s = sep;
 | |
|             return -1;
 | |
|           };
 | |
| 
 | |
|         *s = sep;
 | |
|       };
 | |
|   return 0;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /* Replace filename variables in FILENAME.  We currently support expansion:
 | |
| 
 | |
|    %p - process ID
 | |
|    %q{ENV} - value of environment variable ENV
 | |
|    */
 | |
| 
 | |
| static char *
 | |
| replace_filename_variables (char *filename)
 | |
| {
 | |
|   char buffer[16];
 | |
|   char empty[] = "";
 | |
|   for (char *p = filename; *p != '\0'; p++)
 | |
|     {
 | |
|       unsigned length = strlen (filename);
 | |
|       if (*p == '%' && *(p + 1) != '\0')
 | |
| 	{
 | |
| 	  unsigned start = p - filename;
 | |
| 	  p++;
 | |
| 	  char *replacement = NULL;
 | |
| 	  switch (*p)
 | |
| 	    {
 | |
| 	    case 'p':
 | |
| 	      sprintf (buffer, "%d", getpid ());
 | |
| 	      replacement = buffer;
 | |
| 	      p++;
 | |
| 	      break;
 | |
| 	    case 'q':
 | |
| 	      if (*(p + 1) == '{')
 | |
| 		{
 | |
| 		  p += 2;
 | |
| 		  char *e = strchr (p, '}');
 | |
| 		  if (e)
 | |
| 		    {
 | |
| 		      *e = '\0';
 | |
| 		      replacement = getenv (p);
 | |
| 		      if (replacement == NULL)
 | |
| 			replacement = empty;
 | |
| 		      p = e + 1;
 | |
| 		    }
 | |
| 		  else
 | |
| 		    return filename;
 | |
| 		}
 | |
| 	      break;
 | |
| 	    default:
 | |
| 	      return filename;
 | |
| 	    }
 | |
| 
 | |
| 	  /* Concat beginning of the path, replacement and
 | |
| 	     ending of the path.  */
 | |
| 	  unsigned end = length - (p - filename);
 | |
| 	  unsigned repl_length = strlen (replacement);
 | |
| 
 | |
| 	  char *buffer = (char *)xmalloc (start + end + repl_length + 1);
 | |
| 	  char *buffer_ptr = buffer;
 | |
| 	  buffer_ptr = (char *)memcpy (buffer_ptr, filename, start);
 | |
| 	  buffer_ptr += start;
 | |
| 	  buffer_ptr = (char *)memcpy (buffer_ptr, replacement, repl_length);
 | |
| 	  buffer_ptr += repl_length;
 | |
| 	  buffer_ptr = (char *)memcpy (buffer_ptr, p, end);
 | |
| 	  buffer_ptr += end;
 | |
| 	  *buffer_ptr = '\0';
 | |
| 
 | |
| 	  free (filename);
 | |
| 	  filename = buffer;
 | |
| 	  p = buffer + start + repl_length;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return filename;
 | |
| }
 | |
| 
 | |
| static void
 | |
| allocate_filename_struct (struct gcov_filename *gf)
 | |
| {
 | |
|   const char *gcov_prefix;
 | |
|   size_t prefix_length;
 | |
|   int strip = 0;
 | |
|   gf->filename = NULL;
 | |
| 
 | |
|   {
 | |
|     /* Check if the level of dirs to strip off specified. */
 | |
|     char *tmp = getenv("GCOV_PREFIX_STRIP");
 | |
|     if (tmp)
 | |
|       {
 | |
|         strip = atoi (tmp);
 | |
|         /* Do not consider negative values. */
 | |
|         if (strip < 0)
 | |
|           strip = 0;
 | |
|       }
 | |
|   }
 | |
|   gf->strip = strip;
 | |
| 
 | |
|   /* Get file name relocation prefix.  Non-absolute values are ignored. */
 | |
|   gcov_prefix = getenv("GCOV_PREFIX");
 | |
|   prefix_length = gcov_prefix ? strlen (gcov_prefix) : 0;
 | |
|   
 | |
|   /* Remove an unnecessary trailing '/' */
 | |
|   if (prefix_length && IS_DIR_SEPARATOR (gcov_prefix[prefix_length - 1]))
 | |
|     prefix_length--;
 | |
| 
 | |
|   /* If no prefix was specified and a prefix stip, then we assume
 | |
|      relative.  */
 | |
|   if (!prefix_length && gf->strip)
 | |
|     {
 | |
|       gcov_prefix = ".";
 | |
|       prefix_length = 1;
 | |
|     }
 | |
| 
 | |
|   /* Allocate and initialize the filename scratch space.  */
 | |
|   if (prefix_length)
 | |
|     {
 | |
|       gf->prefix = (char *) xmalloc (prefix_length + 1);
 | |
|       char *p = (char *) memcpy (gf->prefix, gcov_prefix, prefix_length);
 | |
|       *(p + prefix_length) = '\0';
 | |
|     }
 | |
|   else
 | |
|     gf->prefix = NULL;
 | |
| }
 | |
| 
 | |
| /* Open a gcda file specified by GI_FILENAME.
 | |
|    Return -1 on error.  Return 0 on success.  */
 | |
| 
 | |
| static int
 | |
| gcov_exit_open_gcda_file (struct gcov_info *gi_ptr,
 | |
| 			  struct gcov_filename *gf)
 | |
| {
 | |
|   const char *fname = gi_ptr->filename;
 | |
|   int append_slash = 0;
 | |
| 
 | |
|   fname = gi_ptr->filename;
 | |
| 
 | |
|   /* Build relocated filename, stripping off leading
 | |
|      directories from the initial filename if requested. */
 | |
|   if (gf->strip > 0)
 | |
|     {
 | |
|       const char *probe = fname;
 | |
|       int level;
 | |
| 
 | |
|       /* Remove a leading separator, without counting it.  */
 | |
|       if (IS_DIR_SEPARATOR (*probe))
 | |
| 	probe++;
 | |
| 
 | |
|       /* Skip selected directory levels.  If we fall off the end, we
 | |
| 	 keep the final part.  */
 | |
|       for (level = gf->strip; *probe && level; probe++)
 | |
|         if (IS_DIR_SEPARATOR (*probe))
 | |
|           {
 | |
|             fname = probe;
 | |
|             level--;
 | |
|           }
 | |
|     }
 | |
| 
 | |
|   /* Update complete filename with stripped original. */
 | |
|   if (gf->prefix)
 | |
|     {
 | |
|       /* Avoid to add multiple drive letters into combined path.  */
 | |
|       if (HAS_DRIVE_SPEC(fname))
 | |
| 	fname += 2;
 | |
| 
 | |
|       if (!IS_DIR_SEPARATOR (*fname))
 | |
| 	append_slash = 1;
 | |
|     }
 | |
| 
 | |
|   size_t prefix_length = gf->prefix ? strlen (gf->prefix) : 0;
 | |
|   gf->filename = (char *) xmalloc (prefix_length + strlen (fname) + 2);
 | |
|   *gf->filename = '\0';
 | |
|   if (prefix_length)
 | |
|     strcat (gf->filename, gf->prefix);
 | |
|   if (append_slash)
 | |
|     *gf->filename++ = '/';
 | |
|   strcat (gf->filename, fname);
 | |
| 
 | |
|   gf->filename = replace_filename_variables (gf->filename);
 | |
| 
 | |
|   if (!gcov_open (gf->filename))
 | |
|     {
 | |
|       /* Open failed likely due to missed directory.
 | |
|          Create directory and retry to open file. */
 | |
|       if (create_file_directory (gf->filename))
 | |
|         {
 | |
|           fprintf (stderr, "profiling:%s:Skip\n", gf->filename);
 | |
|           return -1;
 | |
|         }
 | |
|       if (!gcov_open (gf->filename))
 | |
|         {
 | |
|           fprintf (stderr, "profiling:%s:Cannot open\n", gf->filename);
 | |
|           return -1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   return 0;
 | |
| }
 |