1/* Routines required for instrumenting a program.  */
2/* Compile this one with gcc.  */
3/* Copyright (C) 1989-2015 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/* A utility function for outputing errors.  */
27
28static int __attribute__((format(printf, 1, 2)))
29gcov_error (const char *fmt, ...)
30{
31  int ret;
32  va_list argp;
33  va_start (argp, fmt);
34  ret = vfprintf (stderr, fmt, argp);
35  va_end (argp);
36  return ret;
37}
38
39/* Make sure path component of the given FILENAME exists, create
40   missing directories. FILENAME must be writable.
41   Returns zero on success, or -1 if an error occurred.  */
42
43static int
44create_file_directory (char *filename)
45{
46#if !defined(TARGET_POSIX_IO) && !defined(_WIN32)
47  (void) filename;
48  return -1;
49#else
50  char *s;
51
52  s = filename;
53
54  if (HAS_DRIVE_SPEC(s))
55    s += 2;
56  if (IS_DIR_SEPARATOR(*s))
57    ++s;
58  for (; *s != '\0'; s++)
59    if (IS_DIR_SEPARATOR(*s))
60      {
61        char sep = *s;
62        *s  = '\0';
63
64        /* Try to make directory if it doesn't already exist.  */
65        if (access (filename, F_OK) == -1
66#ifdef TARGET_POSIX_IO
67            && mkdir (filename, 0755) == -1
68#else
69#ifdef mkdir
70#undef mkdir
71#endif
72            && mkdir (filename) == -1
73#endif
74            /* The directory might have been made by another process.  */
75            && errno != EEXIST)
76          {
77            gcov_error ("profiling:%s:Cannot create directory\n", filename);
78            *s = sep;
79            return -1;
80          };
81
82        *s = sep;
83      };
84  return 0;
85#endif
86}
87
88static void
89allocate_filename_struct (struct gcov_filename *gf)
90{
91  const char *gcov_prefix;
92  size_t prefix_length;
93  int strip = 0;
94
95  {
96    /* Check if the level of dirs to strip off specified. */
97    char *tmp = getenv("GCOV_PREFIX_STRIP");
98    if (tmp)
99      {
100        strip = atoi (tmp);
101        /* Do not consider negative values. */
102        if (strip < 0)
103          strip = 0;
104      }
105  }
106  gf->strip = strip;
107
108  /* Get file name relocation prefix.  Non-absolute values are ignored. */
109  gcov_prefix = getenv("GCOV_PREFIX");
110  prefix_length = gcov_prefix ? strlen (gcov_prefix) : 0;
111
112  /* Remove an unnecessary trailing '/' */
113  if (prefix_length && IS_DIR_SEPARATOR (gcov_prefix[prefix_length - 1]))
114    prefix_length--;
115
116  /* If no prefix was specified and a prefix stip, then we assume
117     relative.  */
118  if (!prefix_length && gf->strip)
119    {
120      gcov_prefix = ".";
121      prefix_length = 1;
122    }
123  gf->prefix = prefix_length;
124
125  /* Allocate and initialize the filename scratch space.  */
126  gf->filename = (char *) xmalloc (gf->max_length + prefix_length + 2);
127  if (prefix_length)
128    memcpy (gf->filename, gcov_prefix, prefix_length);
129}
130
131/* Open a gcda file specified by GI_FILENAME.
132   Return -1 on error.  Return 0 on success.  */
133
134static int
135gcov_exit_open_gcda_file (struct gcov_info *gi_ptr,
136			  struct gcov_filename *gf)
137{
138  const char *fname = gi_ptr->filename;
139  char *dst = gf->filename + gf->prefix;
140
141  fname = gi_ptr->filename;
142
143  /* Build relocated filename, stripping off leading
144     directories from the initial filename if requested. */
145  if (gf->strip > 0)
146    {
147      const char *probe = fname;
148      int level;
149
150      /* Remove a leading separator, without counting it.  */
151      if (IS_DIR_SEPARATOR (*probe))
152	probe++;
153
154      /* Skip selected directory levels.  If we fall off the end, we
155	 keep the final part.  */
156      for (level = gf->strip; *probe && level; probe++)
157        if (IS_DIR_SEPARATOR (*probe))
158          {
159            fname = probe;
160            level--;
161          }
162    }
163
164  /* Update complete filename with stripped original. */
165  if (gf->prefix)
166    {
167      /* Avoid to add multiple drive letters into combined path.  */
168      if (HAS_DRIVE_SPEC(fname))
169	fname += 2;
170
171      if (!IS_DIR_SEPARATOR (*fname))
172	*dst++ = '/';
173    }
174  strcpy (dst, fname);
175
176  if (!gcov_open (gf->filename))
177    {
178      /* Open failed likely due to missed directory.
179         Create directory and retry to open file. */
180      if (create_file_directory (gf->filename))
181        {
182          fprintf (stderr, "profiling:%s:Skip\n", gf->filename);
183          return -1;
184        }
185      if (!gcov_open (gf->filename))
186        {
187          fprintf (stderr, "profiling:%s:Cannot open\n", gf->filename);
188          return -1;
189        }
190    }
191
192  return 0;
193}
194