Viewing file: libgcov-driver-system.c (8.15 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/* Routines required for instrumenting a program. */ /* Compile this one with gcc. */ /* Copyright (C) 1989-2022 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, 0777) == -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 = replacement != NULL ? strlen (replacement) : 0;
char *buffer = (char *)xmalloc (start + end + repl_length + 1); char *buffer_ptr = buffer; buffer_ptr = (char *)memcpy (buffer_ptr, filename, start); buffer_ptr += start; if (replacement != NULL) 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) { int append_slash = 0; const char *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; }
|