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 
writeFile(FILE * File)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 
writeFileWithName(const char * OutputName)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 
setFilename(const char * Filename,int OwnsFilename)79 static void setFilename(const char *Filename, int OwnsFilename) {
80   if (__llvm_profile_OwnsFilename)
81     free(UNCONST(__llvm_profile_CurrentFilename));
82 
83   __llvm_profile_CurrentFilename = Filename;
84   __llvm_profile_OwnsFilename = OwnsFilename;
85 }
86 
truncateCurrentFile(void)87 static void truncateCurrentFile(void) {
88   const char *Filename;
89   FILE *File;
90 
91   Filename = __llvm_profile_CurrentFilename;
92   if (!Filename || !Filename[0])
93     return;
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 
setDefaultFilename(void)102 static void setDefaultFilename(void) { setFilename("default.profraw", 0); }
103 
104 int getpid(void);
setFilenameFromEnvironment(void)105 static int setFilenameFromEnvironment(void) {
106   const char *Filename = getenv("LLVM_PROFILE_FILE");
107 #define MAX_PID_SIZE 16
108   char PidChars[MAX_PID_SIZE] = {0};
109   int NumPids = 0, PidLength = 0;
110   char *Allocated;
111   int I, J;
112 
113   if (!Filename || !Filename[0])
114     return -1;
115 
116   /* Check the filename for "%p", which indicates a pid-substitution. */
117   for (I = 0; Filename[I]; ++I)
118     if (Filename[I] == '%' && Filename[++I] == 'p')
119       if (!NumPids++) {
120         PidLength = snprintf(PidChars, MAX_PID_SIZE, "%d", getpid());
121         if (PidLength <= 0)
122           return -1;
123       }
124   if (!NumPids) {
125     setFilename(Filename, 0);
126     return 0;
127   }
128 
129   /* Allocate enough space for the substituted filename. */
130   Allocated = malloc(I + NumPids*(PidLength - 2) + 1);
131   if (!Allocated)
132     return -1;
133 
134   /* Construct the new filename. */
135   for (I = 0, J = 0; Filename[I]; ++I)
136     if (Filename[I] == '%') {
137       if (Filename[++I] == 'p') {
138         memcpy(Allocated + J, PidChars, PidLength);
139         J += PidLength;
140       }
141       /* Drop any unknown substitutions. */
142     } else
143       Allocated[J++] = Filename[I];
144   Allocated[J] = 0;
145 
146   /* Use the computed name. */
147   setFilename(Allocated, 1);
148   return 0;
149 }
150 
setFilenameAutomatically(void)151 static void setFilenameAutomatically(void) {
152   if (!setFilenameFromEnvironment())
153     return;
154 
155   setDefaultFilename();
156 }
157 
158 __attribute__((visibility("hidden")))
__llvm_profile_initialize_file(void)159 void __llvm_profile_initialize_file(void) {
160   /* Check if the filename has been initialized. */
161   if (__llvm_profile_CurrentFilename)
162     return;
163 
164   /* Detect the filename and truncate. */
165   setFilenameAutomatically();
166   truncateCurrentFile();
167 }
168 
169 __attribute__((visibility("hidden")))
__llvm_profile_set_filename(const char * Filename)170 void __llvm_profile_set_filename(const char *Filename) {
171   setFilename(Filename, 0);
172   truncateCurrentFile();
173 }
174 
175 __attribute__((visibility("hidden")))
__llvm_profile_write_file(void)176 int __llvm_profile_write_file(void) {
177   int rc;
178 
179   /* Check the filename. */
180   if (!__llvm_profile_CurrentFilename)
181     return -1;
182 
183   /* Write the file. */
184   rc = writeFileWithName(__llvm_profile_CurrentFilename);
185   if (rc && getenv("LLVM_PROFILE_VERBOSE_ERRORS"))
186     fprintf(stderr, "LLVM Profile: Failed to write file \"%s\": %s\n",
187             __llvm_profile_CurrentFilename, strerror(errno));
188   return rc;
189 }
190 
writeFileWithoutReturn(void)191 static void writeFileWithoutReturn(void) {
192   __llvm_profile_write_file();
193 }
194 
195 __attribute__((visibility("hidden")))
__llvm_profile_register_write_file_atexit(void)196 int __llvm_profile_register_write_file_atexit(void) {
197   static int HasBeenRegistered = 0;
198 
199   if (HasBeenRegistered)
200     return 0;
201 
202   HasBeenRegistered = 1;
203   return atexit(writeFileWithoutReturn);
204 }
205