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