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 <errno.h> 12#include <stdio.h> 13#include <stdlib.h> 14#include <string.h> 15 16#define UNCONST(ptr) ((void *)(uintptr_t)(ptr)) 17 18static int writeFile(FILE *File) { 19 /* Match logic in __llvm_profile_write_buffer(). */ 20 const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); 21 const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); 22 const uint64_t *CountersBegin = __llvm_profile_begin_counters(); 23 const uint64_t *CountersEnd = __llvm_profile_end_counters(); 24 const char *NamesBegin = __llvm_profile_begin_names(); 25 const char *NamesEnd = __llvm_profile_end_names(); 26 27 /* Calculate size of sections. */ 28 const uint64_t DataSize = DataEnd - DataBegin; 29 const uint64_t CountersSize = CountersEnd - CountersBegin; 30 const uint64_t NamesSize = NamesEnd - NamesBegin; 31 const uint64_t Padding = sizeof(uint64_t) - NamesSize % sizeof(uint64_t); 32 33 /* Enough zeroes for padding. */ 34 const char Zeroes[sizeof(uint64_t)] = {0}; 35 36 /* Create the header. */ 37 uint64_t Header[PROFILE_HEADER_SIZE]; 38 Header[0] = __llvm_profile_get_magic(); 39 Header[1] = __llvm_profile_get_version(); 40 Header[2] = DataSize; 41 Header[3] = CountersSize; 42 Header[4] = NamesSize; 43 Header[5] = (uintptr_t)CountersBegin; 44 Header[6] = (uintptr_t)NamesBegin; 45 46 /* Write the data. */ 47#define CHECK_fwrite(Data, Size, Length, File) \ 48 do { if (fwrite(Data, Size, Length, File) != Length) return -1; } while (0) 49 CHECK_fwrite(Header, sizeof(uint64_t), PROFILE_HEADER_SIZE, File); 50 CHECK_fwrite(DataBegin, sizeof(__llvm_profile_data), DataSize, File); 51 CHECK_fwrite(CountersBegin, sizeof(uint64_t), CountersSize, File); 52 CHECK_fwrite(NamesBegin, sizeof(char), NamesSize, File); 53 CHECK_fwrite(Zeroes, sizeof(char), Padding, File); 54#undef CHECK_fwrite 55 56 return 0; 57} 58 59static int writeFileWithName(const char *OutputName) { 60 int RetVal; 61 FILE *OutputFile; 62 if (!OutputName || !OutputName[0]) 63 return -1; 64 65 /* Append to the file to support profiling multiple shared objects. */ 66 OutputFile = fopen(OutputName, "a"); 67 if (!OutputFile) 68 return -1; 69 70 RetVal = writeFile(OutputFile); 71 72 fclose(OutputFile); 73 return RetVal; 74} 75 76__attribute__((weak)) int __llvm_profile_OwnsFilename = 0; 77__attribute__((weak)) const char *__llvm_profile_CurrentFilename = NULL; 78 79static void setFilename(const char *Filename, int OwnsFilename) { 80 if (__llvm_profile_OwnsFilename) 81 free(UNCONST(__llvm_profile_CurrentFilename)); 82 83 __llvm_profile_CurrentFilename = Filename; 84 __llvm_profile_OwnsFilename = OwnsFilename; 85} 86 87static void truncateCurrentFile(void) { 88 const char *Filename; 89 FILE *File; 90 91 Filename = __llvm_profile_CurrentFilename; 92 if (!Filename || !Filename[0]) 93 return; 94 95 /* Truncate the file. Later we'll reopen and append. */ 96 File = fopen(Filename, "w"); 97 if (!File) 98 return; 99 fclose(File); 100} 101 102static void setDefaultFilename(void) { setFilename("default.profraw", 0); } 103 104int getpid(void); 105static int setFilenameFromEnvironment(void) { 106 const char *Filename = getenv("LLVM_PROFILE_FILE"); 107#define MAX_PID_SIZE 16 108 char PidChars[MAX_PID_SIZE] = {0}; 109 int NumPids = 0, PidLength = 0; 110 char *Allocated; 111 int I, J; 112 113 if (!Filename || !Filename[0]) 114 return -1; 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 151static void setFilenameAutomatically(void) { 152 if (!setFilenameFromEnvironment()) 153 return; 154 155 setDefaultFilename(); 156} 157 158__attribute__((visibility("hidden"))) 159void __llvm_profile_initialize_file(void) { 160 /* Check if the filename has been initialized. */ 161 if (__llvm_profile_CurrentFilename) 162 return; 163 164 /* Detect the filename and truncate. */ 165 setFilenameAutomatically(); 166 truncateCurrentFile(); 167} 168 169__attribute__((visibility("hidden"))) 170void __llvm_profile_set_filename(const char *Filename) { 171 setFilename(Filename, 0); 172 truncateCurrentFile(); 173} 174 175__attribute__((visibility("hidden"))) 176int __llvm_profile_write_file(void) { 177 int rc; 178 179 /* Check the filename. */ 180 if (!__llvm_profile_CurrentFilename) 181 return -1; 182 183 /* Write the file. */ 184 rc = writeFileWithName(__llvm_profile_CurrentFilename); 185 if (rc && getenv("LLVM_PROFILE_VERBOSE_ERRORS")) 186 fprintf(stderr, "LLVM Profile: Failed to write file \"%s\": %s\n", 187 __llvm_profile_CurrentFilename, strerror(errno)); 188 return rc; 189} 190 191static void writeFileWithoutReturn(void) { 192 __llvm_profile_write_file(); 193} 194 195__attribute__((visibility("hidden"))) 196int __llvm_profile_register_write_file_atexit(void) { 197 static int HasBeenRegistered = 0; 198 199 if (HasBeenRegistered) 200 return 0; 201 202 HasBeenRegistered = 1; 203 return atexit(writeFileWithoutReturn); 204} 205