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