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