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