123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- /* 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;
- }
|