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