]> git.proxmox.com Git - rustc.git/blob - src/compiler-rt/lib/profile/InstrProfilingFile.c
Imported Upstream version 1.6.0+dfsg1
[rustc.git] / src / compiler-rt / lib / profile / InstrProfilingFile.c
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 __llvm_profile_header Header;
39 Header.Magic = __llvm_profile_get_magic();
40 Header.Version = __llvm_profile_get_version();
41 Header.DataSize = DataSize;
42 Header.CountersSize = CountersSize;
43 Header.NamesSize = NamesSize;
44 Header.CountersDelta = (uintptr_t)CountersBegin;
45 Header.NamesDelta = (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(__llvm_profile_header), 1, 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 free(Copy);
94 }
95
96 /* Truncate the file. Later we'll reopen and append. */
97 File = fopen(Filename, "w");
98 if (!File)
99 return;
100 fclose(File);
101 }
102
103 static void setFilename(const char *Filename, int OwnsFilename) {
104 /* Check if this is a new filename and therefore needs truncation. */
105 int NewFile = !__llvm_profile_CurrentFilename ||
106 (Filename && strcmp(Filename, __llvm_profile_CurrentFilename));
107 if (__llvm_profile_OwnsFilename)
108 free(UNCONST(__llvm_profile_CurrentFilename));
109
110 __llvm_profile_CurrentFilename = Filename;
111 __llvm_profile_OwnsFilename = OwnsFilename;
112
113 /* If not a new file, append to support profiling multiple shared objects. */
114 if (NewFile)
115 truncateCurrentFile();
116 }
117
118 static void resetFilenameToDefault(void) { setFilename("default.profraw", 0); }
119
120 int getpid(void);
121 static int setFilenamePossiblyWithPid(const char *Filename) {
122 #define MAX_PID_SIZE 16
123 char PidChars[MAX_PID_SIZE] = {0};
124 int NumPids = 0, PidLength = 0;
125 char *Allocated;
126 int I, J;
127
128 /* Reset filename on NULL, except with env var which is checked by caller. */
129 if (!Filename) {
130 resetFilenameToDefault();
131 return 0;
132 }
133
134 /* Check the filename for "%p", which indicates a pid-substitution. */
135 for (I = 0; Filename[I]; ++I)
136 if (Filename[I] == '%' && Filename[++I] == 'p')
137 if (!NumPids++) {
138 PidLength = snprintf(PidChars, MAX_PID_SIZE, "%d", getpid());
139 if (PidLength <= 0)
140 return -1;
141 }
142 if (!NumPids) {
143 setFilename(Filename, 0);
144 return 0;
145 }
146
147 /* Allocate enough space for the substituted filename. */
148 Allocated = malloc(I + NumPids*(PidLength - 2) + 1);
149 if (!Allocated)
150 return -1;
151
152 /* Construct the new filename. */
153 for (I = 0, J = 0; Filename[I]; ++I)
154 if (Filename[I] == '%') {
155 if (Filename[++I] == 'p') {
156 memcpy(Allocated + J, PidChars, PidLength);
157 J += PidLength;
158 }
159 /* Drop any unknown substitutions. */
160 } else
161 Allocated[J++] = Filename[I];
162 Allocated[J] = 0;
163
164 /* Use the computed name. */
165 setFilename(Allocated, 1);
166 return 0;
167 }
168
169 static int setFilenameFromEnvironment(void) {
170 const char *Filename = getenv("LLVM_PROFILE_FILE");
171
172 if (!Filename || !Filename[0])
173 return -1;
174
175 return setFilenamePossiblyWithPid(Filename);
176 }
177
178 static void setFilenameAutomatically(void) {
179 if (!setFilenameFromEnvironment())
180 return;
181
182 resetFilenameToDefault();
183 }
184
185 __attribute__((visibility("hidden")))
186 void __llvm_profile_initialize_file(void) {
187 /* Check if the filename has been initialized. */
188 if (__llvm_profile_CurrentFilename)
189 return;
190
191 /* Detect the filename and truncate. */
192 setFilenameAutomatically();
193 }
194
195 __attribute__((visibility("hidden")))
196 void __llvm_profile_set_filename(const char *Filename) {
197 setFilenamePossiblyWithPid(Filename);
198 }
199
200 __attribute__((visibility("hidden")))
201 void __llvm_profile_override_default_filename(const char *Filename) {
202 /* If the env var is set, skip setting filename from argument. */
203 const char *Env_Filename = getenv("LLVM_PROFILE_FILE");
204 if (Env_Filename && Env_Filename[0])
205 return;
206 setFilenamePossiblyWithPid(Filename);
207 }
208
209 __attribute__((visibility("hidden")))
210 int __llvm_profile_write_file(void) {
211 int rc;
212
213 /* Check the filename. */
214 if (!__llvm_profile_CurrentFilename)
215 return -1;
216
217 /* Write the file. */
218 rc = writeFileWithName(__llvm_profile_CurrentFilename);
219 if (rc && getenv("LLVM_PROFILE_VERBOSE_ERRORS"))
220 fprintf(stderr, "LLVM Profile: Failed to write file \"%s\": %s\n",
221 __llvm_profile_CurrentFilename, strerror(errno));
222 return rc;
223 }
224
225 static void writeFileWithoutReturn(void) {
226 __llvm_profile_write_file();
227 }
228
229 __attribute__((visibility("hidden")))
230 int __llvm_profile_register_write_file_atexit(void) {
231 static int HasBeenRegistered = 0;
232
233 if (HasBeenRegistered)
234 return 0;
235
236 HasBeenRegistered = 1;
237 return atexit(writeFileWithoutReturn);
238 }