111
|
1 /* Routines required for instrumenting a program. */
|
|
2 /* Compile this one with gcc. */
|
145
|
3 /* Copyright (C) 1989-2020 Free Software Foundation, Inc.
|
111
|
4
|
|
5 This file is part of GCC.
|
|
6
|
|
7 GCC is free software; you can redistribute it and/or modify it under
|
|
8 the terms of the GNU General Public License as published by the Free
|
|
9 Software Foundation; either version 3, or (at your option) any later
|
|
10 version.
|
|
11
|
|
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
15 for more details.
|
|
16
|
|
17 Under Section 7 of GPL version 3, you are granted additional
|
|
18 permissions described in the GCC Runtime Library Exception, version
|
|
19 3.1, as published by the Free Software Foundation.
|
|
20
|
|
21 You should have received a copy of the GNU General Public License and
|
|
22 a copy of the GCC Runtime Library Exception along with this program;
|
|
23 see 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. */
|
|
31 FILE *__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
|
|
37 static FILE *
|
|
38 get_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
|
|
58 static int __attribute__((format(printf, 1, 2)))
|
|
59 gcov_error (const char *fmt, ...)
|
|
60 {
|
|
61 int ret;
|
|
62 va_list argp;
|
|
63
|
|
64 va_start (argp, fmt);
|
131
|
65 FILE *f = get_gcov_error_file ();
|
|
66 ret = vfprintf (f, fmt, argp);
|
111
|
67 va_end (argp);
|
131
|
68
|
|
69 if (getenv ("GCOV_EXIT_AT_ERROR"))
|
|
70 {
|
|
71 fprintf (f, "profiling:exiting after an error\n");
|
|
72 exit (1);
|
|
73 }
|
|
74
|
111
|
75 return ret;
|
|
76 }
|
|
77
|
|
78 #if !IN_GCOV_TOOL
|
|
79 static void
|
|
80 gcov_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
|
|
94 static int
|
|
95 create_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
|
131
|
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
|
|
145 static char *
|
|
146 replace_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);
|
145
|
189 unsigned repl_length = replacement != NULL ? strlen (replacement) : 0;
|
131
|
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;
|
145
|
195 if (replacement != NULL)
|
|
196 buffer_ptr = (char *)memcpy (buffer_ptr, replacement, repl_length);
|
131
|
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
|
111
|
211 static void
|
|
212 allocate_filename_struct (struct gcov_filename *gf)
|
|
213 {
|
|
214 const char *gcov_prefix;
|
|
215 size_t prefix_length;
|
|
216 int strip = 0;
|
131
|
217 gf->filename = NULL;
|
111
|
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)
|
131
|
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;
|
111
|
257 }
|
|
258
|
|
259 /* Open a gcda file specified by GI_FILENAME.
|
|
260 Return -1 on error. Return 0 on success. */
|
|
261
|
|
262 static int
|
|
263 gcov_exit_open_gcda_file (struct gcov_info *gi_ptr,
|
|
264 struct gcov_filename *gf)
|
|
265 {
|
145
|
266 int append_slash = 0;
|
111
|
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))
|
131
|
298 append_slash = 1;
|
111
|
299 }
|
131
|
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);
|
111
|
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 }
|