blob: 0102a2533fdee4a7786b52419525584708963f5f [file] [log] [blame]
Stephen Hines2d1fdb22014-05-28 23:58:16 -07001/*===- 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"
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -070011#include <errno.h>
Stephen Hines2d1fdb22014-05-28 23:58:16 -070012#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15
16#define UNCONST(ptr) ((void *)(uintptr_t)(ptr))
17
18static int writeFile(FILE *File) {
19 /* Match logic in __llvm_profile_write_buffer(). */
Stephen Hines6d186232014-11-26 17:56:19 -080020 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();
Stephen Hines2d1fdb22014-05-28 23:58:16 -070026
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
59static 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
Stephen Hines2d1fdb22014-05-28 23:58:16 -070079static void truncateCurrentFile(void) {
Stephen Hines86277eb2015-03-23 12:06:32 -070080 const char *Filename;
81 FILE *File;
82
83 Filename = __llvm_profile_CurrentFilename;
Stephen Hines2d1fdb22014-05-28 23:58:16 -070084 if (!Filename || !Filename[0])
85 return;
86
87 /* Truncate the file. Later we'll reopen and append. */
Stephen Hines86277eb2015-03-23 12:06:32 -070088 File = fopen(Filename, "w");
Stephen Hines2d1fdb22014-05-28 23:58:16 -070089 if (!File)
90 return;
91 fclose(File);
92}
93
Pirama Arumuga Nainarcdce50b2015-07-01 12:26:56 -070094static 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
109static void resetFilenameToDefault(void) { setFilename("default.profraw", 0); }
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700110
111int getpid(void);
Pirama Arumuga Nainarcdce50b2015-07-01 12:26:56 -0700112static int setFilenamePossiblyWithPid(const char *Filename) {
Stephen Hines86277eb2015-03-23 12:06:32 -0700113#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
Pirama Arumuga Nainarcdce50b2015-07-01 12:26:56 -0700119 /* Reset filename on NULL, except with env var which is checked by caller. */
120 if (!Filename) {
121 resetFilenameToDefault();
122 return 0;
123 }
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700124
125 /* Check the filename for "%p", which indicates a pid-substitution. */
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700126 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. */
Stephen Hines86277eb2015-03-23 12:06:32 -0700139 Allocated = malloc(I + NumPids*(PidLength - 2) + 1);
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700140 if (!Allocated)
141 return -1;
142
143 /* Construct the new filename. */
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700144 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
Pirama Arumuga Nainarcdce50b2015-07-01 12:26:56 -0700160static 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
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700169static void setFilenameAutomatically(void) {
170 if (!setFilenameFromEnvironment())
171 return;
172
Pirama Arumuga Nainarcdce50b2015-07-01 12:26:56 -0700173 resetFilenameToDefault();
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700174}
175
176__attribute__((visibility("hidden")))
177void __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();
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700184}
185
186__attribute__((visibility("hidden")))
187void __llvm_profile_set_filename(const char *Filename) {
Pirama Arumuga Nainarcdce50b2015-07-01 12:26:56 -0700188 setFilenamePossiblyWithPid(Filename);
189}
190
191__attribute__((visibility("hidden")))
192void __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);
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700198}
199
200__attribute__((visibility("hidden")))
201int __llvm_profile_write_file(void) {
Stephen Hines86277eb2015-03-23 12:06:32 -0700202 int rc;
203
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700204 /* Check the filename. */
205 if (!__llvm_profile_CurrentFilename)
206 return -1;
207
208 /* Write the file. */
Stephen Hines86277eb2015-03-23 12:06:32 -0700209 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;
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700214}
215
216static void writeFileWithoutReturn(void) {
217 __llvm_profile_write_file();
218}
219
220__attribute__((visibility("hidden")))
221int __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}