1/* Routines required for instrumenting a program. */ 2/* Compile this one with gcc. */ 3/* Copyright (C) 1989-2020 Free Software Foundation, Inc. 4 5This file is part of GCC. 6 7GCC is free software; you can redistribute it and/or modify it under 8the terms of the GNU General Public License as published by the Free 9Software Foundation; either version 3, or (at your option) any later 10version. 11 12GCC is distributed in the hope that it will be useful, but WITHOUT ANY 13WARRANTY; without even the implied warranty of MERCHANTABILITY or 14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15for more details. 16 17Under Section 7 of GPL version 3, you are granted additional 18permissions described in the GCC Runtime Library Exception, version 193.1, as published by the Free Software Foundation. 20 21You should have received a copy of the GNU General Public License and 22a copy of the GCC Runtime Library Exception along with this program; 23see 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. */ 31FILE *__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 37static FILE * 38get_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 58static int __attribute__((format(printf, 1, 2))) 59gcov_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 79static void 80gcov_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 94static int 95create_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, 0755) == -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 145static char * 146replace_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 211static void 212allocate_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 262static int 263gcov_exit_open_gcda_file (struct gcov_info *gi_ptr, 264 struct gcov_filename *gf) 265{ 266 int append_slash = 0; 267 const char *fname = gi_ptr->filename; 268 269 /* Build relocated filename, stripping off leading 270 directories from the initial filename if requested. */ 271 if (gf->strip > 0) 272 { 273 const char *probe = fname; 274 int level; 275 276 /* Remove a leading separator, without counting it. */ 277 if (IS_DIR_SEPARATOR (*probe)) 278 probe++; 279 280 /* Skip selected directory levels. If we fall off the end, we 281 keep the final part. */ 282 for (level = gf->strip; *probe && level; probe++) 283 if (IS_DIR_SEPARATOR (*probe)) 284 { 285 fname = probe; 286 level--; 287 } 288 } 289 290 /* Update complete filename with stripped original. */ 291 if (gf->prefix) 292 { 293 /* Avoid to add multiple drive letters into combined path. */ 294 if (HAS_DRIVE_SPEC(fname)) 295 fname += 2; 296 297 if (!IS_DIR_SEPARATOR (*fname)) 298 append_slash = 1; 299 } 300 301 size_t prefix_length = gf->prefix ? strlen (gf->prefix) : 0; 302 gf->filename = (char *) xmalloc (prefix_length + strlen (fname) + 2); 303 *gf->filename = '\0'; 304 if (prefix_length) 305 strcat (gf->filename, gf->prefix); 306 if (append_slash) 307 *gf->filename++ = '/'; 308 strcat (gf->filename, fname); 309 310 gf->filename = replace_filename_variables (gf->filename); 311 312 if (!gcov_open (gf->filename)) 313 { 314 /* Open failed likely due to missed directory. 315 Create directory and retry to open file. */ 316 if (create_file_directory (gf->filename)) 317 { 318 fprintf (stderr, "profiling:%s:Skip\n", gf->filename); 319 return -1; 320 } 321 if (!gcov_open (gf->filename)) 322 { 323 fprintf (stderr, "profiling:%s:Cannot open\n", gf->filename); 324 return -1; 325 } 326 } 327 328 return 0; 329} 330