1 /*===- InstrProfilingFile.c - Write instrumentation to a file -------------===*\
2 |*
3 |*                     The LLVM Compiler Infrastructure
4 |*
5 |* This file is distributed under the University of Illinois Open Source
6 |* License. See LICENSE.TXT for details.
7 |*
8 \*===----------------------------------------------------------------------===*/
9 
10 #include "InstrProfiling.h"
11 #include "InstrProfilingUtil.h"
12 #include <errno.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 
17 #define UNCONST(ptr) ((void *)(uintptr_t)(ptr))
18 
19 static int writeFile(FILE *File) {
20   /* Match logic in __llvm_profile_write_buffer(). */
21   const __llvm_profile_data *DataBegin = __llvm_profile_begin_data();
22   const __llvm_profile_data *DataEnd = __llvm_profile_end_data();
23   const uint64_t *CountersBegin = __llvm_profile_begin_counters();
24   const uint64_t *CountersEnd   = __llvm_profile_end_counters();
25   const char *NamesBegin = __llvm_profile_begin_names();
26   const char *NamesEnd   = __llvm_profile_end_names();
27 
28   /* Calculate size of sections. */
29   const uint64_t DataSize = DataEnd - DataBegin;
30   const uint64_t CountersSize = CountersEnd - CountersBegin;
31   const uint64_t NamesSize = NamesEnd - NamesBegin;
32   const uint64_t Padding = sizeof(uint64_t) - NamesSize % sizeof(uint64_t);
33 
34   /* Enough zeroes for padding. */
35   const char Zeroes[sizeof(uint64_t)] = {0};
36 
37   /* Create the header. */
38   __llvm_profile_header Header;
39   Header.Magic = __llvm_profile_get_magic();
40   Header.Version = __llvm_profile_get_version();
41   Header.DataSize = DataSize;
42   Header.CountersSize = CountersSize;
43   Header.NamesSize = NamesSize;
44   Header.CountersDelta = (uintptr_t)CountersBegin;
45   Header.NamesDelta = (uintptr_t)NamesBegin;
46 
47   /* Write the data. */
48 #define CHECK_fwrite(Data, Size, Length, File) \
49   do { if (fwrite(Data, Size, Length, File) != Length) return -1; } while (0)
50   CHECK_fwrite(&Header,        sizeof(__llvm_profile_header), 1, File);
51   CHECK_fwrite(DataBegin,     sizeof(__llvm_profile_data), DataSize, File);
52   CHECK_fwrite(CountersBegin, sizeof(uint64_t), CountersSize, File);
53   CHECK_fwrite(NamesBegin,    sizeof(char), NamesSize, File);
54   CHECK_fwrite(Zeroes,        sizeof(char), Padding, File);
55 #undef CHECK_fwrite
56 
57   return 0;
58 }
59 
60 static int writeFileWithName(const char *OutputName) {
61   int RetVal;
62   FILE *OutputFile;
63   if (!OutputName || !OutputName[0])
64     return -1;
65 
66   /* Append to the file to support profiling multiple shared objects. */
67   OutputFile = fopen(OutputName, "a");
68   if (!OutputFile)
69     return -1;
70 
71   RetVal = writeFile(OutputFile);
72 
73   fclose(OutputFile);
74   return RetVal;
75 }
76 
77 __attribute__((weak)) int __llvm_profile_OwnsFilename = 0;
78 __attribute__((weak)) const char *__llvm_profile_CurrentFilename = NULL;
79 
80 static void truncateCurrentFile(void) {
81   const char *Filename;
82   FILE *File;
83 
84   Filename = __llvm_profile_CurrentFilename;
85   if (!Filename || !Filename[0])
86     return;
87 
88   /* Create the directory holding the file, if needed. */
89   if (strchr(Filename, '/')) {
90     char *Copy = malloc(strlen(Filename) + 1);
91     strcpy(Copy, Filename);
92     __llvm_profile_recursive_mkdir(Copy);
93     free(Copy);
94   }
95 
96   /* Truncate the file.  Later we'll reopen and append. */
97   File = fopen(Filename, "w");
98   if (!File)
99     return;
100   fclose(File);
101 }
102 
103 static void setFilename(const char *Filename, int OwnsFilename) {
104   /* Check if this is a new filename and therefore needs truncation. */
105   int NewFile = !__llvm_profile_CurrentFilename ||
106       (Filename && strcmp(Filename, __llvm_profile_CurrentFilename));
107   if (__llvm_profile_OwnsFilename)
108     free(UNCONST(__llvm_profile_CurrentFilename));
109 
110   __llvm_profile_CurrentFilename = Filename;
111   __llvm_profile_OwnsFilename = OwnsFilename;
112 
113   /* If not a new file, append to support profiling multiple shared objects. */
114   if (NewFile)
115     truncateCurrentFile();
116 }
117 
118 static void resetFilenameToDefault(void) { setFilename("default.profraw", 0); }
119 
120 int getpid(void);
121 static int setFilenamePossiblyWithPid(const char *Filename) {
122 #define MAX_PID_SIZE 16
123   char PidChars[MAX_PID_SIZE] = {0};
124   int NumPids = 0, PidLength = 0;
125   char *Allocated;
126   int I, J;
127 
128   /* Reset filename on NULL, except with env var which is checked by caller. */
129   if (!Filename) {
130     resetFilenameToDefault();
131     return 0;
132   }
133 
134   /* Check the filename for "%p", which indicates a pid-substitution. */
135   for (I = 0; Filename[I]; ++I)
136     if (Filename[I] == '%' && Filename[++I] == 'p')
137       if (!NumPids++) {
138         PidLength = snprintf(PidChars, MAX_PID_SIZE, "%d", getpid());
139         if (PidLength <= 0)
140           return -1;
141       }
142   if (!NumPids) {
143     setFilename(Filename, 0);
144     return 0;
145   }
146 
147   /* Allocate enough space for the substituted filename. */
148   Allocated = malloc(I + NumPids*(PidLength - 2) + 1);
149   if (!Allocated)
150     return -1;
151 
152   /* Construct the new filename. */
153   for (I = 0, J = 0; Filename[I]; ++I)
154     if (Filename[I] == '%') {
155       if (Filename[++I] == 'p') {
156         memcpy(Allocated + J, PidChars, PidLength);
157         J += PidLength;
158       }
159       /* Drop any unknown substitutions. */
160     } else
161       Allocated[J++] = Filename[I];
162   Allocated[J] = 0;
163 
164   /* Use the computed name. */
165   setFilename(Allocated, 1);
166   return 0;
167 }
168 
169 static int setFilenameFromEnvironment(void) {
170   const char *Filename = getenv("LLVM_PROFILE_FILE");
171 
172   if (!Filename || !Filename[0])
173     return -1;
174 
175   return setFilenamePossiblyWithPid(Filename);
176 }
177 
178 static void setFilenameAutomatically(void) {
179   if (!setFilenameFromEnvironment())
180     return;
181 
182   resetFilenameToDefault();
183 }
184 
185 __attribute__((visibility("hidden")))
186 void __llvm_profile_initialize_file(void) {
187   /* Check if the filename has been initialized. */
188   if (__llvm_profile_CurrentFilename)
189     return;
190 
191   /* Detect the filename and truncate. */
192   setFilenameAutomatically();
193 }
194 
195 __attribute__((visibility("hidden")))
196 void __llvm_profile_set_filename(const char *Filename) {
197   setFilenamePossiblyWithPid(Filename);
198 }
199 
200 __attribute__((visibility("hidden")))
201 void __llvm_profile_override_default_filename(const char *Filename) {
202   /* If the env var is set, skip setting filename from argument. */
203   const char *Env_Filename = getenv("LLVM_PROFILE_FILE");
204   if (Env_Filename && Env_Filename[0])
205     return;
206   setFilenamePossiblyWithPid(Filename);
207 }
208 
209 __attribute__((visibility("hidden")))
210 int __llvm_profile_write_file(void) {
211   int rc;
212 
213   /* Check the filename. */
214   if (!__llvm_profile_CurrentFilename)
215     return -1;
216 
217   /* Write the file. */
218   rc = writeFileWithName(__llvm_profile_CurrentFilename);
219   if (rc && getenv("LLVM_PROFILE_VERBOSE_ERRORS"))
220     fprintf(stderr, "LLVM Profile: Failed to write file \"%s\": %s\n",
221             __llvm_profile_CurrentFilename, strerror(errno));
222   return rc;
223 }
224 
225 static void writeFileWithoutReturn(void) {
226   __llvm_profile_write_file();
227 }
228 
229 __attribute__((visibility("hidden")))
230 int __llvm_profile_register_write_file_atexit(void) {
231   static int HasBeenRegistered = 0;
232 
233   if (HasBeenRegistered)
234     return 0;
235 
236   HasBeenRegistered = 1;
237   return atexit(writeFileWithoutReturn);
238 }
239