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