InstrProfilingFile.c revision 1.2 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 #ifdef _LIBC
11 #include "namespace.h"
12 #endif
13
14 #include "InstrProfiling.h"
15 #include "InstrProfilingInternal.h"
16 #include "InstrProfilingUtil.h"
17 #include <errno.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #define UNCONST(ptr) ((void *)(uintptr_t)(ptr))
23
24 /* Return 1 if there is an error, otherwise return 0. */
25 static uint32_t fileWriter(ProfDataIOVec *IOVecs, uint32_t NumIOVecs,
26 void **WriterCtx) {
27 uint32_t I;
28 FILE *File = (FILE *)*WriterCtx;
29 for (I = 0; I < NumIOVecs; I++) {
30 if (fwrite(IOVecs[I].Data, IOVecs[I].ElmSize, IOVecs[I].NumElm, File) !=
31 IOVecs[I].NumElm)
32 return 1;
33 }
34 return 0;
35 }
36
37 COMPILER_RT_VISIBILITY ProfBufferIO *
38 llvmCreateBufferIOInternal(void *File, uint32_t BufferSz) {
39 CallocHook = calloc;
40 FreeHook = free;
41 return llvmCreateBufferIO(fileWriter, File, BufferSz);
42 }
43
44 static int writeFile(FILE *File) {
45 const char *BufferSzStr = 0;
46 uint64_t ValueDataSize = 0;
47 struct ValueProfData **ValueDataArray =
48 __llvm_profile_gather_value_data(&ValueDataSize);
49 FreeHook = &free;
50 CallocHook = &calloc;
51 BufferSzStr = getenv("LLVM_VP_BUFFER_SIZE");
52 if (BufferSzStr && BufferSzStr[0])
53 VPBufferSize = atoi(BufferSzStr);
54 return llvmWriteProfData(fileWriter, File, ValueDataArray, ValueDataSize);
55 }
56
57 static int writeFileWithName(const char *OutputName) {
58 int RetVal;
59 FILE *OutputFile;
60 if (!OutputName || !OutputName[0])
61 return -1;
62
63 /* Append to the file to support profiling multiple shared objects. */
64 OutputFile = fopen(OutputName, "ab");
65 if (!OutputFile)
66 return -1;
67
68 RetVal = writeFile(OutputFile);
69
70 fclose(OutputFile);
71 return RetVal;
72 }
73
74 COMPILER_RT_WEAK int __llvm_profile_OwnsFilename = 0;
75 COMPILER_RT_WEAK const char *__llvm_profile_CurrentFilename = NULL;
76
77 static void truncateCurrentFile(void) {
78 const char *Filename;
79 FILE *File;
80
81 Filename = __llvm_profile_CurrentFilename;
82 if (!Filename || !Filename[0])
83 return;
84
85 /* Create the directory holding the file, if needed. */
86 if (strchr(Filename, '/')) {
87 char *Copy = malloc(strlen(Filename) + 1);
88 strcpy(Copy, Filename);
89 __llvm_profile_recursive_mkdir(Copy);
90 free(Copy);
91 }
92
93 /* Truncate the file. Later we'll reopen and append. */
94 File = fopen(Filename, "w");
95 if (!File)
96 return;
97 fclose(File);
98 }
99
100 static void setFilename(const char *Filename, int OwnsFilename) {
101 /* Check if this is a new filename and therefore needs truncation. */
102 int NewFile = !__llvm_profile_CurrentFilename ||
103 (Filename && strcmp(Filename, __llvm_profile_CurrentFilename));
104 if (__llvm_profile_OwnsFilename)
105 free(UNCONST(__llvm_profile_CurrentFilename));
106
107 __llvm_profile_CurrentFilename = Filename;
108 __llvm_profile_OwnsFilename = OwnsFilename;
109
110 /* If not a new file, append to support profiling multiple shared objects. */
111 if (NewFile)
112 truncateCurrentFile();
113 }
114
115 static void resetFilenameToDefault(void) { setFilename("default.profraw", 0); }
116
117 int getpid(void);
118 static int setFilenamePossiblyWithPid(const char *Filename) {
119 #define MAX_PID_SIZE 16
120 char PidChars[MAX_PID_SIZE] = {0};
121 int NumPids = 0, PidLength = 0;
122 char *Allocated;
123 int I, J;
124
125 /* Reset filename on NULL, except with env var which is checked by caller. */
126 if (!Filename) {
127 resetFilenameToDefault();
128 return 0;
129 }
130
131 /* Check the filename for "%p", which indicates a pid-substitution. */
132 for (I = 0; Filename[I]; ++I)
133 if (Filename[I] == '%' && Filename[++I] == 'p')
134 if (!NumPids++) {
135 PidLength = snprintf(PidChars, MAX_PID_SIZE, "%d", getpid());
136 if (PidLength <= 0)
137 return -1;
138 }
139 if (!NumPids) {
140 setFilename(Filename, 0);
141 return 0;
142 }
143
144 /* Allocate enough space for the substituted filename. */
145 Allocated = malloc(I + NumPids*(PidLength - 2) + 1);
146 if (!Allocated)
147 return -1;
148
149 /* Construct the new filename. */
150 for (I = 0, J = 0; Filename[I]; ++I)
151 if (Filename[I] == '%') {
152 if (Filename[++I] == 'p') {
153 memcpy(Allocated + J, PidChars, PidLength);
154 J += PidLength;
155 }
156 /* Drop any unknown substitutions. */
157 } else
158 Allocated[J++] = Filename[I];
159 Allocated[J] = 0;
160
161 /* Use the computed name. */
162 setFilename(Allocated, 1);
163 return 0;
164 }
165
166 static int setFilenameFromEnvironment(void) {
167 const char *Filename = getenv("LLVM_PROFILE_FILE");
168
169 if (!Filename || !Filename[0])
170 return -1;
171
172 return setFilenamePossiblyWithPid(Filename);
173 }
174
175 static void setFilenameAutomatically(void) {
176 if (!setFilenameFromEnvironment())
177 return;
178
179 resetFilenameToDefault();
180 }
181
182 COMPILER_RT_VISIBILITY
183 void __llvm_profile_initialize_file(void) {
184 /* Check if the filename has been initialized. */
185 if (__llvm_profile_CurrentFilename)
186 return;
187
188 /* Detect the filename and truncate. */
189 setFilenameAutomatically();
190 }
191
192 COMPILER_RT_VISIBILITY
193 void __llvm_profile_set_filename(const char *Filename) {
194 setFilenamePossiblyWithPid(Filename);
195 }
196
197 COMPILER_RT_VISIBILITY
198 void __llvm_profile_override_default_filename(const char *Filename) {
199 /* If the env var is set, skip setting filename from argument. */
200 const char *Env_Filename = getenv("LLVM_PROFILE_FILE");
201 if (Env_Filename && Env_Filename[0])
202 return;
203 setFilenamePossiblyWithPid(Filename);
204 }
205
206 COMPILER_RT_VISIBILITY
207 int __llvm_profile_write_file(void) {
208 int rc;
209
210 GetEnvHook = &getenv;
211 /* Check the filename. */
212 if (!__llvm_profile_CurrentFilename) {
213 PROF_ERR("LLVM Profile: Failed to write file : %s\n", "Filename not set");
214 return -1;
215 }
216
217 /* Check if there is llvm/runtime version mismatch. */
218 if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) {
219 PROF_ERR("LLVM Profile: runtime and instrumentation version mismatch : "
220 "expected %d, but get %d\n",
221 INSTR_PROF_RAW_VERSION,
222 (int)GET_VERSION(__llvm_profile_get_version()));
223 return -1;
224 }
225
226 /* Write the file. */
227 rc = writeFileWithName(__llvm_profile_CurrentFilename);
228 if (rc)
229 PROF_ERR("LLVM Profile: Failed to write file \"%s\": %s\n",
230 __llvm_profile_CurrentFilename, strerror(errno));
231 return rc;
232 }
233
234 static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); }
235
236 COMPILER_RT_VISIBILITY
237 int __llvm_profile_register_write_file_atexit(void) {
238 static int HasBeenRegistered = 0;
239
240 if (HasBeenRegistered)
241 return 0;
242
243 HasBeenRegistered = 1;
244 return atexit(writeFileWithoutReturn);
245 }
246