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 23 #define UNCONST(ptr) ((void *)(uintptr_t)(ptr)) 24 25 /* Return 1 if there is an error, otherwise return 0. */ 26 static uint32_t fileWriter(ProfDataIOVec *IOVecs, uint32_t NumIOVecs, 27 void **WriterCtx) { 28 uint32_t I; 29 FILE *File = (FILE *)*WriterCtx; 30 for (I = 0; I < NumIOVecs; I++) { 31 if (fwrite(IOVecs[I].Data, IOVecs[I].ElmSize, IOVecs[I].NumElm, File) != 32 IOVecs[I].NumElm) 33 return 1; 34 } 35 return 0; 36 } 37 38 COMPILER_RT_VISIBILITY ProfBufferIO * 39 lprofCreateBufferIOInternal(void *File, uint32_t BufferSz) { 40 FreeHook = &free; 41 DynamicBufferIOBuffer = (uint8_t *)calloc(BufferSz, 1); 42 VPBufferSize = BufferSz; 43 return lprofCreateBufferIO(fileWriter, File); 44 } 45 46 static void setupIOBuffer() { 47 const char *BufferSzStr = 0; 48 BufferSzStr = getenv("LLVM_VP_BUFFER_SIZE"); 49 if (BufferSzStr && BufferSzStr[0]) { 50 VPBufferSize = atoi(BufferSzStr); 51 DynamicBufferIOBuffer = (uint8_t *)calloc(VPBufferSize, 1); 52 } 53 } 54 55 static int writeFile(FILE *File) { 56 FreeHook = &free; 57 setupIOBuffer(); 58 return lprofWriteData(fileWriter, File, lprofGetVPDataReader()); 59 } 60 61 static int writeFileWithName(const char *OutputName) { 62 int RetVal; 63 FILE *OutputFile; 64 if (!OutputName || !OutputName[0]) 65 return -1; 66 67 /* Append to the file to support profiling multiple shared objects. */ 68 OutputFile = fopen(OutputName, "ab"); 69 if (!OutputFile) 70 return -1; 71 72 RetVal = writeFile(OutputFile); 73 74 fclose(OutputFile); 75 return RetVal; 76 } 77 78 COMPILER_RT_WEAK int __llvm_profile_OwnsFilename = 0; 79 COMPILER_RT_WEAK const char *__llvm_profile_CurrentFilename = NULL; 80 81 static void truncateCurrentFile(void) { 82 const char *Filename; 83 FILE *File; 84 85 Filename = __llvm_profile_CurrentFilename; 86 if (!Filename || !Filename[0]) 87 return; 88 89 /* Create the directory holding the file, if needed. */ 90 if (strchr(Filename, '/') || strchr(Filename, '\\')) { 91 char *Copy = (char *)COMPILER_RT_ALLOCA(strlen(Filename) + 1); 92 strcpy(Copy, Filename); 93 __llvm_profile_recursive_mkdir(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, NumHosts = 0, HostNameLength = 0; 125 char *Allocated; 126 int I, J; 127 char Hostname[COMPILER_RT_MAX_HOSTLEN]; 128 129 /* Reset filename on NULL, except with env var which is checked by caller. */ 130 if (!Filename) { 131 resetFilenameToDefault(); 132 return 0; 133 } 134 135 /* Check the filename for "%p", which indicates a pid-substitution. */ 136 for (I = 0; Filename[I]; ++I) 137 if (Filename[I] == '%') { 138 if (Filename[++I] == 'p') { 139 if (!NumPids++) { 140 PidLength = snprintf(PidChars, MAX_PID_SIZE, "%d", getpid()); 141 if (PidLength <= 0) 142 return -1; 143 } 144 } else if (Filename[I] == 'h') { 145 if (!NumHosts++) 146 if (COMPILER_RT_GETHOSTNAME(Hostname, COMPILER_RT_MAX_HOSTLEN)) 147 return -1; 148 HostNameLength = strlen(Hostname); 149 } 150 } 151 152 if (!(NumPids || NumHosts)) { 153 setFilename(Filename, 0); 154 return 0; 155 } 156 157 /* Allocate enough space for the substituted filename. */ 158 Allocated = malloc(I + NumPids*(PidLength - 2) + 159 NumHosts*(HostNameLength - 2) + 1); 160 if (!Allocated) 161 return -1; 162 163 /* Construct the new filename. */ 164 for (I = 0, J = 0; Filename[I]; ++I) 165 if (Filename[I] == '%') { 166 if (Filename[++I] == 'p') { 167 memcpy(Allocated + J, PidChars, PidLength); 168 J += PidLength; 169 } 170 else if (Filename[I] == 'h') { 171 memcpy(Allocated + J, Hostname, HostNameLength); 172 J += HostNameLength; 173 } 174 /* Drop any unknown substitutions. */ 175 } else 176 Allocated[J++] = Filename[I]; 177 Allocated[J] = 0; 178 179 /* Use the computed name. */ 180 setFilename(Allocated, 1); 181 return 0; 182 } 183 184 static int setFilenameFromEnvironment(void) { 185 const char *Filename = getenv("LLVM_PROFILE_FILE"); 186 187 if (!Filename || !Filename[0]) 188 return -1; 189 190 return setFilenamePossiblyWithPid(Filename); 191 } 192 193 static void setFilenameAutomatically(void) { 194 if (!setFilenameFromEnvironment()) 195 return; 196 197 resetFilenameToDefault(); 198 } 199 200 COMPILER_RT_VISIBILITY 201 void __llvm_profile_initialize_file(void) { 202 /* Check if the filename has been initialized. */ 203 if (__llvm_profile_CurrentFilename) 204 return; 205 206 /* Detect the filename and truncate. */ 207 setFilenameAutomatically(); 208 } 209 210 COMPILER_RT_VISIBILITY 211 void __llvm_profile_set_filename(const char *Filename) { 212 setFilenamePossiblyWithPid(Filename); 213 } 214 215 COMPILER_RT_VISIBILITY 216 void __llvm_profile_override_default_filename(const char *Filename) { 217 /* If the env var is set, skip setting filename from argument. */ 218 const char *Env_Filename = getenv("LLVM_PROFILE_FILE"); 219 if (Env_Filename && Env_Filename[0]) 220 return; 221 setFilenamePossiblyWithPid(Filename); 222 } 223 224 COMPILER_RT_VISIBILITY 225 int __llvm_profile_write_file(void) { 226 int rc; 227 228 GetEnvHook = &getenv; 229 /* Check the filename. */ 230 if (!__llvm_profile_CurrentFilename) { 231 PROF_ERR("Failed to write file : %s\n", "Filename not set"); 232 return -1; 233 } 234 235 /* Check if there is llvm/runtime version mismatch. */ 236 if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) { 237 PROF_ERR("Runtime and instrumentation version mismatch : " 238 "expected %d, but get %d\n", 239 INSTR_PROF_RAW_VERSION, 240 (int)GET_VERSION(__llvm_profile_get_version())); 241 return -1; 242 } 243 244 /* Write the file. */ 245 rc = writeFileWithName(__llvm_profile_CurrentFilename); 246 if (rc) 247 PROF_ERR("Failed to write file \"%s\": %s\n", 248 __llvm_profile_CurrentFilename, strerror(errno)); 249 return rc; 250 } 251 252 static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); } 253 254 COMPILER_RT_VISIBILITY 255 int __llvm_profile_register_write_file_atexit(void) { 256 static int HasBeenRegistered = 0; 257 258 if (HasBeenRegistered) 259 return 0; 260 261 lprofSetupValueProfiler(); 262 263 HasBeenRegistered = 1; 264 return atexit(writeFileWithoutReturn); 265 } 266