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 18 #define UNCONST(ptr) ((void *)(uintptr_t)(ptr)) 19 20 #ifdef _MSC_VER 21 #define snprintf _snprintf 22 #endif 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