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