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