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 "InstrProfilingInternal.h"
12 #include "InstrProfilingUtil.h"
13 #include <errno.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #ifdef _MSC_VER
18 /* For _alloca. */
19 #include <malloc.h>
20 #endif
21 
22 #define MAX_PID_SIZE 16
23 /* Data structure holding the result of parsed filename pattern.  */
24 typedef struct lprofFilename {
25   /* File name string possibly with %p or %h specifiers. */
26   const char *FilenamePat;
27   char PidChars[MAX_PID_SIZE];
28   char Hostname[COMPILER_RT_MAX_HOSTLEN];
29   unsigned NumPids;
30   unsigned NumHosts;
31 } lprofFilename;
32 
33 lprofFilename lprofCurFilename = {0, {0}, {0}, 0, 0};
34 
35 int getpid(void);
36 static int getCurFilenameLength();
37 static const char *getCurFilename(char *FilenameBuf);
38 
39 /* Return 1 if there is an error, otherwise return  0.  */
40 static uint32_t fileWriter(ProfDataIOVec *IOVecs, uint32_t NumIOVecs,
41                            void **WriterCtx) {
42   uint32_t I;
43   FILE *File = (FILE *)*WriterCtx;
44   for (I = 0; I < NumIOVecs; I++) {
45     if (fwrite(IOVecs[I].Data, IOVecs[I].ElmSize, IOVecs[I].NumElm, File) !=
46         IOVecs[I].NumElm)
47       return 1;
48   }
49   return 0;
50 }
51 
52 COMPILER_RT_VISIBILITY ProfBufferIO *
53 lprofCreateBufferIOInternal(void *File, uint32_t BufferSz) {
54   FreeHook = &free;
55   DynamicBufferIOBuffer = (uint8_t *)calloc(BufferSz, 1);
56   VPBufferSize = BufferSz;
57   return lprofCreateBufferIO(fileWriter, File);
58 }
59 
60 static void setupIOBuffer() {
61   const char *BufferSzStr = 0;
62   BufferSzStr = getenv("LLVM_VP_BUFFER_SIZE");
63   if (BufferSzStr && BufferSzStr[0]) {
64     VPBufferSize = atoi(BufferSzStr);
65     DynamicBufferIOBuffer = (uint8_t *)calloc(VPBufferSize, 1);
66   }
67 }
68 
69 /* Write profile data to file \c OutputName.  */
70 static int writeFile(const char *OutputName) {
71   int RetVal;
72   FILE *OutputFile;
73 
74   /* Append to the file to support profiling multiple shared objects. */
75   OutputFile = fopen(OutputName, "ab");
76   if (!OutputFile)
77     return -1;
78 
79   FreeHook = &free;
80   setupIOBuffer();
81   RetVal = lprofWriteData(fileWriter, OutputFile, lprofGetVPDataReader());
82 
83   fclose(OutputFile);
84   return RetVal;
85 }
86 
87 static void truncateCurrentFile(void) {
88   const char *Filename;
89   char *FilenameBuf;
90   FILE *File;
91   int Length;
92 
93   Length = getCurFilenameLength();
94   FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1);
95   Filename = getCurFilename(FilenameBuf);
96   if (!Filename)
97     return;
98 
99   /* Create the directory holding the file, if needed. */
100   if (strchr(Filename, '/') || strchr(Filename, '\\')) {
101     char *Copy = (char *)COMPILER_RT_ALLOCA(Length + 1);
102     strncpy(Copy, Filename, Length + 1);
103     __llvm_profile_recursive_mkdir(Copy);
104   }
105 
106   /* Truncate the file.  Later we'll reopen and append. */
107   File = fopen(Filename, "w");
108   if (!File)
109     return;
110   fclose(File);
111 }
112 
113 static void resetFilenameToDefault(void) {
114   memset(&lprofCurFilename, 0, sizeof(lprofCurFilename));
115   lprofCurFilename.FilenamePat = "default.profraw";
116 }
117 
118 /* Parses the pattern string \p FilenamePat and store the result to
119  * lprofcurFilename structure. */
120 
121 static int parseFilenamePattern(const char *FilenamePat) {
122   int NumPids = 0, NumHosts = 0, I;
123   char *PidChars = &lprofCurFilename.PidChars[0];
124   char *Hostname = &lprofCurFilename.Hostname[0];
125 
126   lprofCurFilename.FilenamePat = FilenamePat;
127   /* Check the filename for "%p", which indicates a pid-substitution. */
128   for (I = 0; FilenamePat[I]; ++I)
129     if (FilenamePat[I] == '%') {
130       if (FilenamePat[++I] == 'p') {
131         if (!NumPids++) {
132           if (snprintf(PidChars, MAX_PID_SIZE, "%d", getpid()) <= 0) {
133             PROF_WARN(
134                 "Unable to parse filename pattern %s. Using the default name.",
135                 FilenamePat);
136             return -1;
137           }
138         }
139       } else if (FilenamePat[I] == 'h') {
140         if (!NumHosts++)
141           if (COMPILER_RT_GETHOSTNAME(Hostname, COMPILER_RT_MAX_HOSTLEN)) {
142             PROF_WARN(
143                 "Unable to parse filename pattern %s. Using the default name.",
144                 FilenamePat);
145             return -1;
146           }
147       }
148     }
149 
150   lprofCurFilename.NumPids = NumPids;
151   lprofCurFilename.NumHosts = NumHosts;
152   return 0;
153 }
154 
155 static void parseAndSetFilename(const char *FilenamePat) {
156   int NewFile;
157   const char *OldFilenamePat = lprofCurFilename.FilenamePat;
158 
159   if (!FilenamePat || parseFilenamePattern(FilenamePat))
160     resetFilenameToDefault();
161 
162   NewFile =
163       !OldFilenamePat || (strcmp(OldFilenamePat, lprofCurFilename.FilenamePat));
164 
165   if (NewFile)
166     truncateCurrentFile();
167 }
168 
169 /* Return buffer length that is required to store the current profile
170  * filename with PID and hostname substitutions. */
171 static int getCurFilenameLength() {
172   if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0])
173     return 0;
174 
175   if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts))
176     return strlen(lprofCurFilename.FilenamePat);
177 
178   return strlen(lprofCurFilename.FilenamePat) +
179          lprofCurFilename.NumPids * (strlen(lprofCurFilename.PidChars) - 2) +
180          lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2);
181 }
182 
183 /* Return the pointer to the current profile file name (after substituting
184  * PIDs and Hostnames in filename pattern. \p FilenameBuf is the buffer
185  * to store the resulting filename. If no substitution is needed, the
186  * current filename pattern string is directly returned. */
187 static const char *getCurFilename(char *FilenameBuf) {
188   int I, J, PidLength, HostNameLength;
189   const char *FilenamePat = lprofCurFilename.FilenamePat;
190 
191   if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0])
192     return 0;
193 
194   if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts))
195     return lprofCurFilename.FilenamePat;
196 
197   PidLength = strlen(lprofCurFilename.PidChars);
198   HostNameLength = strlen(lprofCurFilename.Hostname);
199   /* Construct the new filename. */
200   for (I = 0, J = 0; FilenamePat[I]; ++I)
201     if (FilenamePat[I] == '%') {
202       if (FilenamePat[++I] == 'p') {
203         memcpy(FilenameBuf + J, lprofCurFilename.PidChars, PidLength);
204         J += PidLength;
205       } else if (FilenamePat[I] == 'h') {
206         memcpy(FilenameBuf + J, lprofCurFilename.Hostname, HostNameLength);
207         J += HostNameLength;
208       }
209       /* Drop any unknown substitutions. */
210     } else
211       FilenameBuf[J++] = FilenamePat[I];
212   FilenameBuf[J] = 0;
213 
214   return FilenameBuf;
215 }
216 
217 /* Returns the pointer to the environment variable
218  * string. Returns null if the env var is not set. */
219 static const char *getFilenamePatFromEnv(void) {
220   const char *Filename = getenv("LLVM_PROFILE_FILE");
221   if (!Filename || !Filename[0])
222     return 0;
223   return Filename;
224 }
225 
226 /* This method is invoked by the runtime initialization hook
227  * InstrProfilingRuntime.o if it is linked in. Both user specified
228  * profile path via -fprofile-instr-generate= and LLVM_PROFILE_FILE
229  * environment variable can override this default value. */
230 COMPILER_RT_VISIBILITY
231 void __llvm_profile_initialize_file(void) {
232   const char *FilenamePat;
233   /* Check if the filename has been initialized. */
234   if (lprofCurFilename.FilenamePat)
235     return;
236 
237   /* Detect the filename and truncate. */
238   FilenamePat = getFilenamePatFromEnv();
239   parseAndSetFilename(FilenamePat);
240 }
241 
242 /* This API is directly called by the user application code. It has the
243  * highest precedence compared with LLVM_PROFILE_FILE environment variable
244  * and command line option -fprofile-instr-generate=<profile_name>.
245  */
246 COMPILER_RT_VISIBILITY
247 void __llvm_profile_set_filename(const char *FilenamePat) {
248   parseAndSetFilename(FilenamePat);
249 }
250 
251 /*
252  * This API is invoked by the global initializers emitted by Clang/LLVM when
253  * -fprofile-instr-generate=<..> is specified (vs -fprofile-instr-generate
254  *  without an argument). This option has lower precedence than the
255  *  LLVM_PROFILE_FILE environment variable.
256  */
257 COMPILER_RT_VISIBILITY
258 void __llvm_profile_override_default_filename(const char *FilenamePat) {
259   /* If the env var is set, skip setting filename from argument. */
260   const char *Env_Filename = getFilenamePatFromEnv();
261   if (Env_Filename)
262     return;
263 
264   parseAndSetFilename(FilenamePat);
265 }
266 
267 /* The public API for writing profile data into the file with name
268  * set by previous calls to __llvm_profile_set_filename or
269  * __llvm_profile_override_default_filename or
270  * __llvm_profile_initialize_file. */
271 COMPILER_RT_VISIBILITY
272 int __llvm_profile_write_file(void) {
273   int rc, Length;
274   const char *Filename;
275   char *FilenameBuf;
276 
277   Length = getCurFilenameLength();
278   FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1);
279   Filename = getCurFilename(FilenameBuf);
280 
281   /* Check the filename. */
282   if (!Filename) {
283     PROF_ERR("Failed to write file : %s\n", "Filename not set");
284     return -1;
285   }
286 
287   /* Check if there is llvm/runtime version mismatch.  */
288   if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) {
289     PROF_ERR("Runtime and instrumentation version mismatch : "
290              "expected %d, but get %d\n",
291              INSTR_PROF_RAW_VERSION,
292              (int)GET_VERSION(__llvm_profile_get_version()));
293     return -1;
294   }
295 
296   /* Write profile data to the file. */
297   rc = writeFile(Filename);
298   if (rc)
299     PROF_ERR("Failed to write file \"%s\": %s\n", Filename, strerror(errno));
300   return rc;
301 }
302 
303 static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); }
304 
305 COMPILER_RT_VISIBILITY
306 int __llvm_profile_register_write_file_atexit(void) {
307   static int HasBeenRegistered = 0;
308 
309   if (HasBeenRegistered)
310     return 0;
311 
312   lprofSetupValueProfiler();
313 
314   HasBeenRegistered = 1;
315   return atexit(writeFileWithoutReturn);
316 }
317