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