]> git.ipfire.org Git - thirdparty/gcc.git/blame - libgcc/libgcov-driver-system.c
gcc/testsuite/
[thirdparty/gcc.git] / libgcc / libgcov-driver-system.c
CommitLineData
ded3d3f8 1/* Routines required for instrumenting a program. */
2/* Compile this one with gcc. */
fbd26352 3/* Copyright (C) 1989-2019 Free Software Foundation, Inc.
ded3d3f8 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
5d4123dc 26#if !IN_GCOV_TOOL
3237fc56 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;
5d4123dc 32#endif
3237fc56 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 *
5d4123dc 38get_gcov_error_file (void)
3237fc56 39{
5d4123dc 40#if IN_GCOV_TOOL
3237fc56 41 return stderr;
42#else
5d4123dc 43 if (!__gcov_error_file)
3237fc56 44 {
5d4123dc 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;
3237fc56 51 }
3237fc56 52 return __gcov_error_file;
53#endif
54}
55
56/* A utility function for outputting errors. */
ded3d3f8 57
58static int __attribute__((format(printf, 1, 2)))
59gcov_error (const char *fmt, ...)
60{
61 int ret;
62 va_list argp;
3237fc56 63
ded3d3f8 64 va_start (argp, fmt);
5522d934 65 FILE *f = get_gcov_error_file ();
66 ret = vfprintf (f, fmt, argp);
ded3d3f8 67 va_end (argp);
5522d934 68
69 if (getenv ("GCOV_EXIT_AT_ERROR"))
70 {
71 fprintf (f, "profiling:exiting after an error\n");
72 exit (1);
73 }
74
ded3d3f8 75 return ret;
76}
77
3237fc56 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
ded3d3f8 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
a698bcaa 120#ifdef mkdir
121#undef mkdir
122#endif
ded3d3f8 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
945d4d55 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);
367a3e90 189 unsigned repl_length = replacement != NULL ? strlen (replacement) : 0;
945d4d55 190
191 char *buffer = (char *)xmalloc (start + end + repl_length + 1);
192 char *buffer_ptr = buffer;
5b24ad4b 193 buffer_ptr = (char *)memcpy (buffer_ptr, filename, start);
194 buffer_ptr += start;
367a3e90 195 if (replacement != NULL)
196 buffer_ptr = (char *)memcpy (buffer_ptr, replacement, repl_length);
5b24ad4b 197 buffer_ptr += repl_length;
198 buffer_ptr = (char *)memcpy (buffer_ptr, p, end);
199 buffer_ptr += end;
945d4d55 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
ded3d3f8 211static void
6401b74d 212allocate_filename_struct (struct gcov_filename *gf)
ded3d3f8 213{
214 const char *gcov_prefix;
ded3d3f8 215 size_t prefix_length;
6401b74d 216 int strip = 0;
5b24ad4b 217 gf->filename = NULL;
ded3d3f8 218
ded3d3f8 219 {
220 /* Check if the level of dirs to strip off specified. */
221 char *tmp = getenv("GCOV_PREFIX_STRIP");
222 if (tmp)
223 {
6401b74d 224 strip = atoi (tmp);
ded3d3f8 225 /* Do not consider negative values. */
6401b74d 226 if (strip < 0)
227 strip = 0;
ded3d3f8 228 }
229 }
6401b74d 230 gf->strip = strip;
ded3d3f8 231
232 /* Get file name relocation prefix. Non-absolute values are ignored. */
233 gcov_prefix = getenv("GCOV_PREFIX");
6401b74d 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--;
ded3d3f8 239
240 /* If no prefix was specified and a prefix stip, then we assume
241 relative. */
6401b74d 242 if (!prefix_length && gf->strip)
ded3d3f8 243 {
244 gcov_prefix = ".";
245 prefix_length = 1;
246 }
ded3d3f8 247
6401b74d 248 /* Allocate and initialize the filename scratch space. */
6401b74d 249 if (prefix_length)
5b24ad4b 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;
ded3d3f8 257}
258
259/* Open a gcda file specified by GI_FILENAME.
260 Return -1 on error. Return 0 on success. */
261
262static int
6401b74d 263gcov_exit_open_gcda_file (struct gcov_info *gi_ptr,
264 struct gcov_filename *gf)
ded3d3f8 265{
5b24ad4b 266 int append_slash = 0;
22eb1ed5 267 const char *fname = gi_ptr->filename;
ded3d3f8 268
ded3d3f8 269 /* Build relocated filename, stripping off leading
270 directories from the initial filename if requested. */
6401b74d 271 if (gf->strip > 0)
ded3d3f8 272 {
6401b74d 273 const char *probe = fname;
274 int level;
ded3d3f8 275
6401b74d 276 /* Remove a leading separator, without counting it. */
277 if (IS_DIR_SEPARATOR (*probe))
278 probe++;
ded3d3f8 279
6401b74d 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))
ded3d3f8 284 {
6401b74d 285 fname = probe;
286 level--;
ded3d3f8 287 }
288 }
289
290 /* Update complete filename with stripped original. */
6401b74d 291 if (gf->prefix)
ded3d3f8 292 {
6401b74d 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))
5b24ad4b 298 append_slash = 1;
ded3d3f8 299 }
5b24ad4b 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);
ded3d3f8 309
945d4d55 310 gf->filename = replace_filename_variables (gf->filename);
311
6401b74d 312 if (!gcov_open (gf->filename))
ded3d3f8 313 {
314 /* Open failed likely due to missed directory.
315 Create directory and retry to open file. */
6401b74d 316 if (create_file_directory (gf->filename))
ded3d3f8 317 {
6401b74d 318 fprintf (stderr, "profiling:%s:Skip\n", gf->filename);
ded3d3f8 319 return -1;
320 }
6401b74d 321 if (!gcov_open (gf->filename))
ded3d3f8 322 {
6401b74d 323 fprintf (stderr, "profiling:%s:Cannot open\n", gf->filename);
ded3d3f8 324 return -1;
325 }
326 }
327
328 return 0;
329}