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 
23 #define UNCONST(ptr) ((void *)(uintptr_t)(ptr))
24 
25 /* Return 1 if there is an error, otherwise return  0.  */
26 static uint32_t fileWriter(ProfDataIOVec *IOVecs, uint32_t NumIOVecs,
27                            void **WriterCtx) {
28   uint32_t I;
29   FILE *File = (FILE *)*WriterCtx;
30   for (I = 0; I < NumIOVecs; I++) {
31     if (fwrite(IOVecs[I].Data, IOVecs[I].ElmSize, IOVecs[I].NumElm, File) !=
32         IOVecs[I].NumElm)
33       return 1;
34   }
35   return 0;
36 }
37 
38 COMPILER_RT_VISIBILITY ProfBufferIO *
39 lprofCreateBufferIOInternal(void *File, uint32_t BufferSz) {
40   FreeHook = &free;
41   DynamicBufferIOBuffer = (uint8_t *)calloc(BufferSz, 1);
42   VPBufferSize = BufferSz;
43   return lprofCreateBufferIO(fileWriter, File);
44 }
45 
46 static void setupIOBuffer() {
47   const char *BufferSzStr = 0;
48   BufferSzStr = getenv("LLVM_VP_BUFFER_SIZE");
49   if (BufferSzStr && BufferSzStr[0]) {
50     VPBufferSize = atoi(BufferSzStr);
51     DynamicBufferIOBuffer = (uint8_t *)calloc(VPBufferSize, 1);
52   }
53 }
54 
55 static int writeFile(FILE *File) {
56   FreeHook = &free;
57   setupIOBuffer();
58   return lprofWriteData(fileWriter, File, lprofGetVPDataReader());
59 }
60 
61 static int writeFileWithName(const char *OutputName) {
62   int RetVal;
63   FILE *OutputFile;
64   if (!OutputName || !OutputName[0])
65     return -1;
66 
67   /* Append to the file to support profiling multiple shared objects. */
68   OutputFile = fopen(OutputName, "ab");
69   if (!OutputFile)
70     return -1;
71 
72   RetVal = writeFile(OutputFile);
73 
74   fclose(OutputFile);
75   return RetVal;
76 }
77 
78 COMPILER_RT_WEAK int __llvm_profile_OwnsFilename = 0;
79 COMPILER_RT_WEAK const char *__llvm_profile_CurrentFilename = NULL;
80 
81 static void truncateCurrentFile(void) {
82   const char *Filename;
83   FILE *File;
84 
85   Filename = __llvm_profile_CurrentFilename;
86   if (!Filename || !Filename[0])
87     return;
88 
89   /* Create the directory holding the file, if needed. */
90   if (strchr(Filename, '/') || strchr(Filename, '\\')) {
91     char *Copy = (char *)COMPILER_RT_ALLOCA(strlen(Filename) + 1);
92     strcpy(Copy, Filename);
93     __llvm_profile_recursive_mkdir(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, NumHosts = 0, HostNameLength = 0;
125   char *Allocated;
126   int I, J;
127   char Hostname[COMPILER_RT_MAX_HOSTLEN];
128 
129   /* Reset filename on NULL, except with env var which is checked by caller. */
130   if (!Filename) {
131     resetFilenameToDefault();
132     return 0;
133   }
134 
135   /* Check the filename for "%p", which indicates a pid-substitution. */
136   for (I = 0; Filename[I]; ++I)
137     if (Filename[I] == '%') {
138       if (Filename[++I] == 'p') {
139         if (!NumPids++) {
140           PidLength = snprintf(PidChars, MAX_PID_SIZE, "%d", getpid());
141           if (PidLength <= 0)
142             return -1;
143         }
144       } else if (Filename[I] == 'h') {
145         if (!NumHosts++)
146           if (COMPILER_RT_GETHOSTNAME(Hostname, COMPILER_RT_MAX_HOSTLEN))
147             return -1;
148           HostNameLength = strlen(Hostname);
149       }
150     }
151 
152   if (!(NumPids || NumHosts)) {
153     setFilename(Filename, 0);
154     return 0;
155   }
156 
157   /* Allocate enough space for the substituted filename. */
158   Allocated = malloc(I + NumPids*(PidLength - 2) +
159                      NumHosts*(HostNameLength - 2) + 1);
160   if (!Allocated)
161     return -1;
162 
163   /* Construct the new filename. */
164   for (I = 0, J = 0; Filename[I]; ++I)
165     if (Filename[I] == '%') {
166       if (Filename[++I] == 'p') {
167         memcpy(Allocated + J, PidChars, PidLength);
168         J += PidLength;
169       }
170       else if (Filename[I] == 'h') {
171         memcpy(Allocated + J, Hostname, HostNameLength);
172         J += HostNameLength;
173       }
174       /* Drop any unknown substitutions. */
175     } else
176       Allocated[J++] = Filename[I];
177   Allocated[J] = 0;
178 
179   /* Use the computed name. */
180   setFilename(Allocated, 1);
181   return 0;
182 }
183 
184 static int setFilenameFromEnvironment(void) {
185   const char *Filename = getenv("LLVM_PROFILE_FILE");
186 
187   if (!Filename || !Filename[0])
188     return -1;
189 
190   return setFilenamePossiblyWithPid(Filename);
191 }
192 
193 static void setFilenameAutomatically(void) {
194   if (!setFilenameFromEnvironment())
195     return;
196 
197   resetFilenameToDefault();
198 }
199 
200 COMPILER_RT_VISIBILITY
201 void __llvm_profile_initialize_file(void) {
202   /* Check if the filename has been initialized. */
203   if (__llvm_profile_CurrentFilename)
204     return;
205 
206   /* Detect the filename and truncate. */
207   setFilenameAutomatically();
208 }
209 
210 COMPILER_RT_VISIBILITY
211 void __llvm_profile_set_filename(const char *Filename) {
212   setFilenamePossiblyWithPid(Filename);
213 }
214 
215 COMPILER_RT_VISIBILITY
216 void __llvm_profile_override_default_filename(const char *Filename) {
217   /* If the env var is set, skip setting filename from argument. */
218   const char *Env_Filename = getenv("LLVM_PROFILE_FILE");
219   if (Env_Filename && Env_Filename[0])
220     return;
221   setFilenamePossiblyWithPid(Filename);
222 }
223 
224 COMPILER_RT_VISIBILITY
225 int __llvm_profile_write_file(void) {
226   int rc;
227 
228   GetEnvHook = &getenv;
229   /* Check the filename. */
230   if (!__llvm_profile_CurrentFilename) {
231     PROF_ERR("Failed to write file : %s\n", "Filename not set");
232     return -1;
233   }
234 
235   /* Check if there is llvm/runtime version mismatch.  */
236   if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) {
237     PROF_ERR("Runtime and instrumentation version mismatch : "
238              "expected %d, but get %d\n",
239              INSTR_PROF_RAW_VERSION,
240              (int)GET_VERSION(__llvm_profile_get_version()));
241     return -1;
242   }
243 
244   /* Write the file. */
245   rc = writeFileWithName(__llvm_profile_CurrentFilename);
246   if (rc)
247     PROF_ERR("Failed to write file \"%s\": %s\n",
248             __llvm_profile_CurrentFilename, strerror(errno));
249   return rc;
250 }
251 
252 static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); }
253 
254 COMPILER_RT_VISIBILITY
255 int __llvm_profile_register_write_file_atexit(void) {
256   static int HasBeenRegistered = 0;
257 
258   if (HasBeenRegistered)
259     return 0;
260 
261   lprofSetupValueProfiler();
262 
263   HasBeenRegistered = 1;
264   return atexit(writeFileWithoutReturn);
265 }
266