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 "InstrProfilingInternal.h" 12 #include "InstrProfilingUtil.h" 13 #include <errno.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #ifdef _MSC_VER 18 /* For _alloca. */ 19 #include <malloc.h> 20 #endif 21 22 #define MAX_PID_SIZE 16 23 /* Data structure holding the result of parsed filename pattern. */ 24 typedef struct lprofFilename { 25 /* File name string possibly with %p or %h specifiers. */ 26 const char *FilenamePat; 27 char PidChars[MAX_PID_SIZE]; 28 char Hostname[COMPILER_RT_MAX_HOSTLEN]; 29 unsigned NumPids; 30 unsigned NumHosts; 31 } lprofFilename; 32 33 lprofFilename lprofCurFilename = {0, {0}, {0}, 0, 0}; 34 35 int getpid(void); 36 static int getCurFilenameLength(); 37 static const char *getCurFilename(char *FilenameBuf); 38 39 /* Return 1 if there is an error, otherwise return 0. */ 40 static uint32_t fileWriter(ProfDataIOVec *IOVecs, uint32_t NumIOVecs, 41 void **WriterCtx) { 42 uint32_t I; 43 FILE *File = (FILE *)*WriterCtx; 44 for (I = 0; I < NumIOVecs; I++) { 45 if (fwrite(IOVecs[I].Data, IOVecs[I].ElmSize, IOVecs[I].NumElm, File) != 46 IOVecs[I].NumElm) 47 return 1; 48 } 49 return 0; 50 } 51 52 COMPILER_RT_VISIBILITY ProfBufferIO * 53 lprofCreateBufferIOInternal(void *File, uint32_t BufferSz) { 54 FreeHook = &free; 55 DynamicBufferIOBuffer = (uint8_t *)calloc(BufferSz, 1); 56 VPBufferSize = BufferSz; 57 return lprofCreateBufferIO(fileWriter, File); 58 } 59 60 static void setupIOBuffer() { 61 const char *BufferSzStr = 0; 62 BufferSzStr = getenv("LLVM_VP_BUFFER_SIZE"); 63 if (BufferSzStr && BufferSzStr[0]) { 64 VPBufferSize = atoi(BufferSzStr); 65 DynamicBufferIOBuffer = (uint8_t *)calloc(VPBufferSize, 1); 66 } 67 } 68 69 /* Write profile data to file \c OutputName. */ 70 static int writeFile(const char *OutputName) { 71 int RetVal; 72 FILE *OutputFile; 73 74 /* Append to the file to support profiling multiple shared objects. */ 75 OutputFile = fopen(OutputName, "ab"); 76 if (!OutputFile) 77 return -1; 78 79 FreeHook = &free; 80 setupIOBuffer(); 81 RetVal = lprofWriteData(fileWriter, OutputFile, lprofGetVPDataReader()); 82 83 fclose(OutputFile); 84 return RetVal; 85 } 86 87 static void truncateCurrentFile(void) { 88 const char *Filename; 89 char *FilenameBuf; 90 FILE *File; 91 int Length; 92 93 Length = getCurFilenameLength(); 94 FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1); 95 Filename = getCurFilename(FilenameBuf); 96 if (!Filename) 97 return; 98 99 /* Create the directory holding the file, if needed. */ 100 if (strchr(Filename, '/') || strchr(Filename, '\\')) { 101 char *Copy = (char *)COMPILER_RT_ALLOCA(Length + 1); 102 strncpy(Copy, Filename, Length + 1); 103 __llvm_profile_recursive_mkdir(Copy); 104 } 105 106 /* Truncate the file. Later we'll reopen and append. */ 107 File = fopen(Filename, "w"); 108 if (!File) 109 return; 110 fclose(File); 111 } 112 113 static void resetFilenameToDefault(void) { 114 memset(&lprofCurFilename, 0, sizeof(lprofCurFilename)); 115 lprofCurFilename.FilenamePat = "default.profraw"; 116 } 117 118 /* Parses the pattern string \p FilenamePat and store the result to 119 * lprofcurFilename structure. */ 120 121 static int parseFilenamePattern(const char *FilenamePat) { 122 int NumPids = 0, NumHosts = 0, I; 123 char *PidChars = &lprofCurFilename.PidChars[0]; 124 char *Hostname = &lprofCurFilename.Hostname[0]; 125 126 lprofCurFilename.FilenamePat = FilenamePat; 127 /* Check the filename for "%p", which indicates a pid-substitution. */ 128 for (I = 0; FilenamePat[I]; ++I) 129 if (FilenamePat[I] == '%') { 130 if (FilenamePat[++I] == 'p') { 131 if (!NumPids++) { 132 if (snprintf(PidChars, MAX_PID_SIZE, "%d", getpid()) <= 0) { 133 PROF_WARN( 134 "Unable to parse filename pattern %s. Using the default name.", 135 FilenamePat); 136 return -1; 137 } 138 } 139 } else if (FilenamePat[I] == 'h') { 140 if (!NumHosts++) 141 if (COMPILER_RT_GETHOSTNAME(Hostname, COMPILER_RT_MAX_HOSTLEN)) { 142 PROF_WARN( 143 "Unable to parse filename pattern %s. Using the default name.", 144 FilenamePat); 145 return -1; 146 } 147 } 148 } 149 150 lprofCurFilename.NumPids = NumPids; 151 lprofCurFilename.NumHosts = NumHosts; 152 return 0; 153 } 154 155 static void parseAndSetFilename(const char *FilenamePat) { 156 int NewFile; 157 const char *OldFilenamePat = lprofCurFilename.FilenamePat; 158 159 if (!FilenamePat || parseFilenamePattern(FilenamePat)) 160 resetFilenameToDefault(); 161 162 NewFile = 163 !OldFilenamePat || (strcmp(OldFilenamePat, lprofCurFilename.FilenamePat)); 164 165 if (NewFile) 166 truncateCurrentFile(); 167 } 168 169 /* Return buffer length that is required to store the current profile 170 * filename with PID and hostname substitutions. */ 171 static int getCurFilenameLength() { 172 if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0]) 173 return 0; 174 175 if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts)) 176 return strlen(lprofCurFilename.FilenamePat); 177 178 return strlen(lprofCurFilename.FilenamePat) + 179 lprofCurFilename.NumPids * (strlen(lprofCurFilename.PidChars) - 2) + 180 lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2); 181 } 182 183 /* Return the pointer to the current profile file name (after substituting 184 * PIDs and Hostnames in filename pattern. \p FilenameBuf is the buffer 185 * to store the resulting filename. If no substitution is needed, the 186 * current filename pattern string is directly returned. */ 187 static const char *getCurFilename(char *FilenameBuf) { 188 int I, J, PidLength, HostNameLength; 189 const char *FilenamePat = lprofCurFilename.FilenamePat; 190 191 if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0]) 192 return 0; 193 194 if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts)) 195 return lprofCurFilename.FilenamePat; 196 197 PidLength = strlen(lprofCurFilename.PidChars); 198 HostNameLength = strlen(lprofCurFilename.Hostname); 199 /* Construct the new filename. */ 200 for (I = 0, J = 0; FilenamePat[I]; ++I) 201 if (FilenamePat[I] == '%') { 202 if (FilenamePat[++I] == 'p') { 203 memcpy(FilenameBuf + J, lprofCurFilename.PidChars, PidLength); 204 J += PidLength; 205 } else if (FilenamePat[I] == 'h') { 206 memcpy(FilenameBuf + J, lprofCurFilename.Hostname, HostNameLength); 207 J += HostNameLength; 208 } 209 /* Drop any unknown substitutions. */ 210 } else 211 FilenameBuf[J++] = FilenamePat[I]; 212 FilenameBuf[J] = 0; 213 214 return FilenameBuf; 215 } 216 217 /* Returns the pointer to the environment variable 218 * string. Returns null if the env var is not set. */ 219 static const char *getFilenamePatFromEnv(void) { 220 const char *Filename = getenv("LLVM_PROFILE_FILE"); 221 if (!Filename || !Filename[0]) 222 return 0; 223 return Filename; 224 } 225 226 /* This method is invoked by the runtime initialization hook 227 * InstrProfilingRuntime.o if it is linked in. Both user specified 228 * profile path via -fprofile-instr-generate= and LLVM_PROFILE_FILE 229 * environment variable can override this default value. */ 230 COMPILER_RT_VISIBILITY 231 void __llvm_profile_initialize_file(void) { 232 const char *FilenamePat; 233 /* Check if the filename has been initialized. */ 234 if (lprofCurFilename.FilenamePat) 235 return; 236 237 /* Detect the filename and truncate. */ 238 FilenamePat = getFilenamePatFromEnv(); 239 parseAndSetFilename(FilenamePat); 240 } 241 242 /* This API is directly called by the user application code. It has the 243 * highest precedence compared with LLVM_PROFILE_FILE environment variable 244 * and command line option -fprofile-instr-generate=<profile_name>. 245 */ 246 COMPILER_RT_VISIBILITY 247 void __llvm_profile_set_filename(const char *FilenamePat) { 248 parseAndSetFilename(FilenamePat); 249 } 250 251 /* 252 * This API is invoked by the global initializers emitted by Clang/LLVM when 253 * -fprofile-instr-generate=<..> is specified (vs -fprofile-instr-generate 254 * without an argument). This option has lower precedence than the 255 * LLVM_PROFILE_FILE environment variable. 256 */ 257 COMPILER_RT_VISIBILITY 258 void __llvm_profile_override_default_filename(const char *FilenamePat) { 259 /* If the env var is set, skip setting filename from argument. */ 260 const char *Env_Filename = getFilenamePatFromEnv(); 261 if (Env_Filename) 262 return; 263 264 parseAndSetFilename(FilenamePat); 265 } 266 267 /* The public API for writing profile data into the file with name 268 * set by previous calls to __llvm_profile_set_filename or 269 * __llvm_profile_override_default_filename or 270 * __llvm_profile_initialize_file. */ 271 COMPILER_RT_VISIBILITY 272 int __llvm_profile_write_file(void) { 273 int rc, Length; 274 const char *Filename; 275 char *FilenameBuf; 276 277 Length = getCurFilenameLength(); 278 FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1); 279 Filename = getCurFilename(FilenameBuf); 280 281 /* Check the filename. */ 282 if (!Filename) { 283 PROF_ERR("Failed to write file : %s\n", "Filename not set"); 284 return -1; 285 } 286 287 /* Check if there is llvm/runtime version mismatch. */ 288 if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) { 289 PROF_ERR("Runtime and instrumentation version mismatch : " 290 "expected %d, but get %d\n", 291 INSTR_PROF_RAW_VERSION, 292 (int)GET_VERSION(__llvm_profile_get_version())); 293 return -1; 294 } 295 296 /* Write profile data to the file. */ 297 rc = writeFile(Filename); 298 if (rc) 299 PROF_ERR("Failed to write file \"%s\": %s\n", Filename, strerror(errno)); 300 return rc; 301 } 302 303 static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); } 304 305 COMPILER_RT_VISIBILITY 306 int __llvm_profile_register_write_file_atexit(void) { 307 static int HasBeenRegistered = 0; 308 309 if (HasBeenRegistered) 310 return 0; 311 312 lprofSetupValueProfiler(); 313 314 HasBeenRegistered = 1; 315 return atexit(writeFileWithoutReturn); 316 } 317