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 <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14
15#define UNCONST(ptr) ((void *)(uintptr_t)(ptr))
16
17static int writeFile(FILE *File) {
18  /* Match logic in __llvm_profile_write_buffer(). */
19  const __llvm_profile_data *DataBegin = __llvm_profile_data_begin();
20  const __llvm_profile_data *DataEnd = __llvm_profile_data_end();
21  const uint64_t *CountersBegin = __llvm_profile_counters_begin();
22  const uint64_t *CountersEnd   = __llvm_profile_counters_end();
23  const char *NamesBegin = __llvm_profile_names_begin();
24  const char *NamesEnd   = __llvm_profile_names_end();
25
26  /* Calculate size of sections. */
27  const uint64_t DataSize = DataEnd - DataBegin;
28  const uint64_t CountersSize = CountersEnd - CountersBegin;
29  const uint64_t NamesSize = NamesEnd - NamesBegin;
30  const uint64_t Padding = sizeof(uint64_t) - NamesSize % sizeof(uint64_t);
31
32  /* Enough zeroes for padding. */
33  const char Zeroes[sizeof(uint64_t)] = {0};
34
35  /* Create the header. */
36  uint64_t Header[PROFILE_HEADER_SIZE];
37  Header[0] = __llvm_profile_get_magic();
38  Header[1] = __llvm_profile_get_version();
39  Header[2] = DataSize;
40  Header[3] = CountersSize;
41  Header[4] = NamesSize;
42  Header[5] = (uintptr_t)CountersBegin;
43  Header[6] = (uintptr_t)NamesBegin;
44
45  /* Write the data. */
46#define CHECK_fwrite(Data, Size, Length, File) \
47  do { if (fwrite(Data, Size, Length, File) != Length) return -1; } while (0)
48  CHECK_fwrite(Header,        sizeof(uint64_t), PROFILE_HEADER_SIZE, File);
49  CHECK_fwrite(DataBegin,     sizeof(__llvm_profile_data), DataSize, File);
50  CHECK_fwrite(CountersBegin, sizeof(uint64_t), CountersSize, File);
51  CHECK_fwrite(NamesBegin,    sizeof(char), NamesSize, File);
52  CHECK_fwrite(Zeroes,        sizeof(char), Padding, File);
53#undef CHECK_fwrite
54
55  return 0;
56}
57
58static int writeFileWithName(const char *OutputName) {
59  int RetVal;
60  FILE *OutputFile;
61  if (!OutputName || !OutputName[0])
62    return -1;
63
64  /* Append to the file to support profiling multiple shared objects. */
65  OutputFile = fopen(OutputName, "a");
66  if (!OutputFile)
67    return -1;
68
69  RetVal = writeFile(OutputFile);
70
71  fclose(OutputFile);
72  return RetVal;
73}
74
75__attribute__((weak)) int __llvm_profile_OwnsFilename = 0;
76__attribute__((weak)) const char *__llvm_profile_CurrentFilename = NULL;
77
78static void setFilename(const char *Filename, int OwnsFilename) {
79  if (__llvm_profile_OwnsFilename)
80    free(UNCONST(__llvm_profile_CurrentFilename));
81
82  __llvm_profile_CurrentFilename = Filename;
83  __llvm_profile_OwnsFilename = OwnsFilename;
84}
85
86static void truncateCurrentFile(void) {
87  const char *Filename = __llvm_profile_CurrentFilename;
88  if (!Filename || !Filename[0])
89    return;
90
91  /* Truncate the file.  Later we'll reopen and append. */
92  FILE *File = fopen(Filename, "w");
93  if (!File)
94    return;
95  fclose(File);
96}
97
98static void setDefaultFilename(void) { setFilename("default.profraw", 0); }
99
100int getpid(void);
101static int setFilenameFromEnvironment(void) {
102  const char *Filename = getenv("LLVM_PROFILE_FILE");
103  if (!Filename || !Filename[0])
104    return -1;
105
106  /* Check the filename for "%p", which indicates a pid-substitution. */
107#define MAX_PID_SIZE 16
108  char PidChars[MAX_PID_SIZE] = {0};
109  int NumPids = 0;
110  int PidLength = 0;
111  int I;
112  for (I = 0; Filename[I]; ++I)
113    if (Filename[I] == '%' && Filename[++I] == 'p')
114      if (!NumPids++) {
115        PidLength = snprintf(PidChars, MAX_PID_SIZE, "%d", getpid());
116        if (PidLength <= 0)
117          return -1;
118      }
119  if (!NumPids) {
120    setFilename(Filename, 0);
121    return 0;
122  }
123
124  /* Allocate enough space for the substituted filename. */
125  char *Allocated = (char*)malloc(I + NumPids*(PidLength - 2) + 1);
126  if (!Allocated)
127    return -1;
128
129  /* Construct the new filename. */
130  int J;
131  for (I = 0, J = 0; Filename[I]; ++I)
132    if (Filename[I] == '%') {
133      if (Filename[++I] == 'p') {
134        memcpy(Allocated + J, PidChars, PidLength);
135        J += PidLength;
136      }
137      /* Drop any unknown substitutions. */
138    } else
139      Allocated[J++] = Filename[I];
140  Allocated[J] = 0;
141
142  /* Use the computed name. */
143  setFilename(Allocated, 1);
144  return 0;
145}
146
147static void setFilenameAutomatically(void) {
148  if (!setFilenameFromEnvironment())
149    return;
150
151  setDefaultFilename();
152}
153
154__attribute__((visibility("hidden")))
155void __llvm_profile_initialize_file(void) {
156  /* Check if the filename has been initialized. */
157  if (__llvm_profile_CurrentFilename)
158    return;
159
160  /* Detect the filename and truncate. */
161  setFilenameAutomatically();
162  truncateCurrentFile();
163}
164
165__attribute__((visibility("hidden")))
166void __llvm_profile_set_filename(const char *Filename) {
167  setFilename(Filename, 0);
168  truncateCurrentFile();
169}
170
171__attribute__((visibility("hidden")))
172int __llvm_profile_write_file(void) {
173  /* Check the filename. */
174  if (!__llvm_profile_CurrentFilename)
175    return -1;
176
177  /* Write the file. */
178  return writeFileWithName(__llvm_profile_CurrentFilename);
179}
180
181static void writeFileWithoutReturn(void) {
182  __llvm_profile_write_file();
183}
184
185__attribute__((visibility("hidden")))
186int __llvm_profile_register_write_file_atexit(void) {
187  static int HasBeenRegistered = 0;
188
189  if (HasBeenRegistered)
190    return 0;
191
192  HasBeenRegistered = 1;
193  return atexit(writeFileWithoutReturn);
194}
195