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