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