1 /* Routines required for instrumenting a program.  */
2 /* Compile this one with gcc.  */
3 /* Copyright (C) 1989-2018 Free Software Foundation, Inc.
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);
65   ret = vfprintf (get_gcov_error_file (), fmt, argp);
66   va_end (argp);
67   return ret;
68 }
69 
70 #if !IN_GCOV_TOOL
71 static void
72 gcov_error_exit (void)
73 {
74   if (__gcov_error_file && __gcov_error_file != stderr)
75     {
76       fclose (__gcov_error_file);
77       __gcov_error_file = NULL;
78     }
79 }
80 #endif
81 
82 /* Make sure path component of the given FILENAME exists, create
83    missing directories. FILENAME must be writable.
84    Returns zero on success, or -1 if an error occurred.  */
85 
86 static int
87 create_file_directory (char *filename)
88 {
89 #if !defined(TARGET_POSIX_IO) && !defined(_WIN32)
90   (void) filename;
91   return -1;
92 #else
93   char *s;
94 
95   s = filename;
96 
97   if (HAS_DRIVE_SPEC(s))
98     s += 2;
99   if (IS_DIR_SEPARATOR(*s))
100     ++s;
101   for (; *s != '\0'; s++)
102     if (IS_DIR_SEPARATOR(*s))
103       {
104         char sep = *s;
105         *s  = '\0';
106 
107         /* Try to make directory if it doesn't already exist.  */
108         if (access (filename, F_OK) == -1
109 #ifdef TARGET_POSIX_IO
110             && mkdir (filename, 0755) == -1
111 #else
112 #ifdef mkdir
113 #undef mkdir
114 #endif
115             && mkdir (filename) == -1
116 #endif
117             /* The directory might have been made by another process.  */
118             && errno != EEXIST)
119           {
120             gcov_error ("profiling:%s:Cannot create directory\n", filename);
121             *s = sep;
122             return -1;
123           };
124 
125         *s = sep;
126       };
127   return 0;
128 #endif
129 }
130 
131 static void
132 allocate_filename_struct (struct gcov_filename *gf)
133 {
134   const char *gcov_prefix;
135   size_t prefix_length;
136   int strip = 0;
137 
138   {
139     /* Check if the level of dirs to strip off specified. */
140     char *tmp = getenv("GCOV_PREFIX_STRIP");
141     if (tmp)
142       {
143         strip = atoi (tmp);
144         /* Do not consider negative values. */
145         if (strip < 0)
146           strip = 0;
147       }
148   }
149   gf->strip = strip;
150 
151   /* Get file name relocation prefix.  Non-absolute values are ignored. */
152   gcov_prefix = getenv("GCOV_PREFIX");
153   prefix_length = gcov_prefix ? strlen (gcov_prefix) : 0;
154 
155   /* Remove an unnecessary trailing '/' */
156   if (prefix_length && IS_DIR_SEPARATOR (gcov_prefix[prefix_length - 1]))
157     prefix_length--;
158 
159   /* If no prefix was specified and a prefix stip, then we assume
160      relative.  */
161   if (!prefix_length && gf->strip)
162     {
163       gcov_prefix = ".";
164       prefix_length = 1;
165     }
166   gf->prefix = prefix_length;
167 
168   /* Allocate and initialize the filename scratch space.  */
169   gf->filename = (char *) xmalloc (gf->max_length + prefix_length + 2);
170   if (prefix_length)
171     memcpy (gf->filename, gcov_prefix, prefix_length);
172 }
173 
174 /* Open a gcda file specified by GI_FILENAME.
175    Return -1 on error.  Return 0 on success.  */
176 
177 static int
178 gcov_exit_open_gcda_file (struct gcov_info *gi_ptr,
179 			  struct gcov_filename *gf)
180 {
181   const char *fname = gi_ptr->filename;
182   char *dst = gf->filename + gf->prefix;
183 
184   fname = gi_ptr->filename;
185 
186   /* Build relocated filename, stripping off leading
187      directories from the initial filename if requested. */
188   if (gf->strip > 0)
189     {
190       const char *probe = fname;
191       int level;
192 
193       /* Remove a leading separator, without counting it.  */
194       if (IS_DIR_SEPARATOR (*probe))
195 	probe++;
196 
197       /* Skip selected directory levels.  If we fall off the end, we
198 	 keep the final part.  */
199       for (level = gf->strip; *probe && level; probe++)
200         if (IS_DIR_SEPARATOR (*probe))
201           {
202             fname = probe;
203             level--;
204           }
205     }
206 
207   /* Update complete filename with stripped original. */
208   if (gf->prefix)
209     {
210       /* Avoid to add multiple drive letters into combined path.  */
211       if (HAS_DRIVE_SPEC(fname))
212 	fname += 2;
213 
214       if (!IS_DIR_SEPARATOR (*fname))
215 	*dst++ = '/';
216     }
217   strcpy (dst, fname);
218 
219   if (!gcov_open (gf->filename))
220     {
221       /* Open failed likely due to missed directory.
222          Create directory and retry to open file. */
223       if (create_file_directory (gf->filename))
224         {
225           fprintf (stderr, "profiling:%s:Skip\n", gf->filename);
226           return -1;
227         }
228       if (!gcov_open (gf->filename))
229         {
230           fprintf (stderr, "profiling:%s:Cannot open\n", gf->filename);
231           return -1;
232         }
233     }
234 
235   return 0;
236 }
237