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