blob: e7996e2828946e8a44a2ef5cb6429193791f27ff [file] [log] [blame]
Duncan P. N. Exon Smithbe0a5e12014-03-21 18:29:15 +00001/*===- InstrProfilingFile.c - Write instrumentation to a file -------------===*\
Duncan P. N. Exon Smith8353a262014-03-19 22:10:27 +00002|*
Chandler Carruth2946cd72019-01-19 08:50:56 +00003|* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4|* See https://llvm.org/LICENSE.txt for license information.
5|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Duncan P. N. Exon Smith8353a262014-03-19 22:10:27 +00006|*
7\*===----------------------------------------------------------------------===*/
8
Petr Hosek47e5fcb2018-07-25 03:01:35 +00009#if !defined(__Fuchsia__)
10
Joerg Sonnenbergeref24b412015-03-09 11:23:29 +000011#include <errno.h>
Duncan P. N. Exon Smithbe0a5e12014-03-21 18:29:15 +000012#include <stdio.h>
13#include <stdlib.h>
Duncan P. N. Exon Smithe5edc882014-03-21 00:27:48 +000014#include <string.h>
Xinliang David Li71eddbf2016-05-20 04:52:27 +000015#ifdef _MSC_VER
Xinliang David Li315e49d2016-05-24 21:29:18 +000016/* For _alloca. */
Xinliang David Li71eddbf2016-05-20 04:52:27 +000017#include <malloc.h>
Xinliang David Lifb320a12016-05-20 05:40:07 +000018#endif
Xinliang David Lie2ce2e02016-06-08 23:43:56 +000019#if defined(_WIN32)
20#include "WindowsMMap.h"
21/* For _chsize_s */
22#include <io.h>
Reid Kleckner963aba32018-04-23 17:05:47 +000023#include <process.h>
Xinliang David Lie2ce2e02016-06-08 23:43:56 +000024#else
25#include <sys/file.h>
26#include <sys/mman.h>
27#include <unistd.h>
28#if defined(__linux__)
29#include <sys/types.h>
30#endif
31#endif
Xinliang David Li71eddbf2016-05-20 04:52:27 +000032
Vedant Kumard7c93362017-12-14 19:01:04 +000033#include "InstrProfiling.h"
34#include "InstrProfilingInternal.h"
35#include "InstrProfilingUtil.h"
36
Xinliang David Li153e8b62016-06-10 20:35:01 +000037/* From where is profile name specified.
38 * The order the enumerators define their
39 * precedence. Re-order them may lead to
Sajjad Mirza6694b2b2019-06-24 21:32:50 +000040 * runtime behavior change. */
Xinliang David Li153e8b62016-06-10 20:35:01 +000041typedef enum ProfileNameSpecifier {
42 PNS_unknown = 0,
43 PNS_default,
44 PNS_command_line,
45 PNS_environment,
46 PNS_runtime_api
47} ProfileNameSpecifier;
48
49static const char *getPNSStr(ProfileNameSpecifier PNS) {
50 switch (PNS) {
51 case PNS_default:
52 return "default setting";
53 case PNS_command_line:
54 return "command line";
55 case PNS_environment:
56 return "environment variable";
57 case PNS_runtime_api:
58 return "runtime API";
59 default:
60 return "Unknown";
61 }
62}
63
Xinliang David Li315e49d2016-05-24 21:29:18 +000064#define MAX_PID_SIZE 16
Ying Yi2c614cf2016-08-10 10:48:02 +000065/* Data structure holding the result of parsed filename pattern. */
Xinliang David Li315e49d2016-05-24 21:29:18 +000066typedef struct lprofFilename {
67 /* File name string possibly with %p or %h specifiers. */
68 const char *FilenamePat;
Xinliang David Li14c91c42016-08-02 19:34:00 +000069 /* A flag indicating if FilenamePat's memory is allocated
70 * by runtime. */
71 unsigned OwnsFilenamePat;
Xinliang David Lieaf238d2016-07-20 04:26:09 +000072 const char *ProfilePathPrefix;
Teresa Johnson73053b22018-07-19 19:03:50 +000073 const char *Filename;
Xinliang David Li315e49d2016-05-24 21:29:18 +000074 char PidChars[MAX_PID_SIZE];
75 char Hostname[COMPILER_RT_MAX_HOSTLEN];
76 unsigned NumPids;
77 unsigned NumHosts;
Xinliang David Lie2ce2e02016-06-08 23:43:56 +000078 /* When in-process merging is enabled, this parameter specifies
79 * the total number of profile data files shared by all the processes
80 * spawned from the same binary. By default the value is 1. If merging
81 * is not enabled, its value should be 0. This parameter is specified
82 * by the %[0-9]m specifier. For instance %2m enables merging using
83 * 2 profile data files. %1m is equivalent to %m. Also %m specifier
84 * can only appear once at the end of the name pattern. */
85 unsigned MergePoolSize;
Xinliang David Li153e8b62016-06-10 20:35:01 +000086 ProfileNameSpecifier PNS;
Xinliang David Li315e49d2016-05-24 21:29:18 +000087} lprofFilename;
Duncan P. N. Exon Smith8353a262014-03-19 22:10:27 +000088
Teresa Johnson73053b22018-07-19 19:03:50 +000089COMPILER_RT_WEAK lprofFilename lprofCurFilename = {0, 0, 0, 0, {0},
90 {0}, 0, 0, 0, PNS_unknown};
Xinliang David Li315e49d2016-05-24 21:29:18 +000091
Sajjad Mirza6694b2b2019-06-24 21:32:50 +000092static int ProfileMergeRequested = 0;
93static int isProfileMergeRequested() { return ProfileMergeRequested; }
94static void setProfileMergeRequested(int EnableMerge) {
95 ProfileMergeRequested = EnableMerge;
96}
97
98static FILE *ProfileFile = NULL;
99static FILE *getProfileFile() { return ProfileFile; }
100static void setProfileFile(FILE *File) { ProfileFile = File; }
101
102COMPILER_RT_VISIBILITY void __llvm_profile_set_file_object(FILE *File,
103 int EnableMerge) {
104 setProfileFile(File);
105 setProfileMergeRequested(EnableMerge);
106}
107
Alex Lorenz341317f2017-08-31 15:51:23 +0000108static int getCurFilenameLength();
Teresa Johnson73053b22018-07-19 19:03:50 +0000109static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf);
Sajjad Mirza6694b2b2019-06-24 21:32:50 +0000110static unsigned doMerging() {
111 return lprofCurFilename.MergePoolSize || isProfileMergeRequested();
112}
Joerg Sonnenbergerb1cc6d52014-05-20 16:37:07 +0000113
Xinliang David Li2d5bf072015-11-21 04:16:42 +0000114/* Return 1 if there is an error, otherwise return 0. */
Xinliang David Li967669f2017-06-27 17:28:01 +0000115static uint32_t fileWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs,
116 uint32_t NumIOVecs) {
Xinliang David Li2d5bf072015-11-21 04:16:42 +0000117 uint32_t I;
Xinliang David Li967669f2017-06-27 17:28:01 +0000118 FILE *File = (FILE *)This->WriterCtx;
Xinliang David Li2d5bf072015-11-21 04:16:42 +0000119 for (I = 0; I < NumIOVecs; I++) {
Xinliang David Lif50cc3e2017-06-28 16:46:06 +0000120 if (IOVecs[I].Data) {
121 if (fwrite(IOVecs[I].Data, IOVecs[I].ElmSize, IOVecs[I].NumElm, File) !=
122 IOVecs[I].NumElm)
123 return 1;
124 } else {
125 if (fseek(File, IOVecs[I].ElmSize * IOVecs[I].NumElm, SEEK_CUR) == -1)
126 return 1;
127 }
Xinliang David Li2d5bf072015-11-21 04:16:42 +0000128 }
129 return 0;
Xinliang David Li1d8d46a2015-11-18 21:08:03 +0000130}
Duncan P. N. Exon Smithcf4bb962014-03-21 18:29:19 +0000131
Manman Rene73ae9a2019-03-08 15:30:56 +0000132/* TODO: make buffer size controllable by an internal option, and compiler can pass the size
133 to runtime via a variable. */
134static uint32_t orderFileWriter(FILE *File, const uint32_t *DataStart) {
135 if (fwrite(DataStart, sizeof(uint32_t), INSTR_ORDER_FILE_BUFFER_SIZE, File) !=
136 INSTR_ORDER_FILE_BUFFER_SIZE)
137 return 1;
138 return 0;
139}
140
Xinliang David Li967669f2017-06-27 17:28:01 +0000141static void initFileWriter(ProfDataWriter *This, FILE *File) {
142 This->Write = fileWriter;
143 This->WriterCtx = File;
144}
145
Xinliang David Licda3bc22015-12-29 23:54:41 +0000146COMPILER_RT_VISIBILITY ProfBufferIO *
Xinliang David Licf1a8d62016-03-06 04:18:13 +0000147lprofCreateBufferIOInternal(void *File, uint32_t BufferSz) {
Xinliang David Li609fae32016-05-13 18:26:26 +0000148 FreeHook = &free;
149 DynamicBufferIOBuffer = (uint8_t *)calloc(BufferSz, 1);
150 VPBufferSize = BufferSz;
Xinliang David Li967669f2017-06-27 17:28:01 +0000151 ProfDataWriter *fileWriter =
152 (ProfDataWriter *)calloc(sizeof(ProfDataWriter), 1);
153 initFileWriter(fileWriter, File);
154 ProfBufferIO *IO = lprofCreateBufferIO(fileWriter);
155 IO->OwnFileWriter = 1;
156 return IO;
Xinliang David Li609fae32016-05-13 18:26:26 +0000157}
158
159static void setupIOBuffer() {
160 const char *BufferSzStr = 0;
161 BufferSzStr = getenv("LLVM_VP_BUFFER_SIZE");
162 if (BufferSzStr && BufferSzStr[0]) {
163 VPBufferSize = atoi(BufferSzStr);
164 DynamicBufferIOBuffer = (uint8_t *)calloc(VPBufferSize, 1);
165 }
Xinliang David Licda3bc22015-12-29 23:54:41 +0000166}
167
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000168/* Read profile data in \c ProfileFile and merge with in-memory
169 profile counters. Returns -1 if there is fatal error, otheriwse
Xinliang David Lif50cc3e2017-06-28 16:46:06 +0000170 0 is returned. Returning 0 does not mean merge is actually
171 performed. If merge is actually done, *MergeDone is set to 1.
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000172*/
Xinliang David Lif50cc3e2017-06-28 16:46:06 +0000173static int doProfileMerging(FILE *ProfileFile, int *MergeDone) {
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000174 uint64_t ProfileFileSize;
175 char *ProfileBuffer;
176
177 if (fseek(ProfileFile, 0L, SEEK_END) == -1) {
178 PROF_ERR("Unable to merge profile data, unable to get size: %s\n",
179 strerror(errno));
180 return -1;
181 }
182 ProfileFileSize = ftell(ProfileFile);
183
184 /* Restore file offset. */
185 if (fseek(ProfileFile, 0L, SEEK_SET) == -1) {
186 PROF_ERR("Unable to merge profile data, unable to rewind: %s\n",
187 strerror(errno));
188 return -1;
189 }
190
191 /* Nothing to merge. */
192 if (ProfileFileSize < sizeof(__llvm_profile_header)) {
193 if (ProfileFileSize)
194 PROF_WARN("Unable to merge profile data: %s\n",
195 "source profile file is too small.");
196 return 0;
197 }
198
199 ProfileBuffer = mmap(NULL, ProfileFileSize, PROT_READ, MAP_SHARED | MAP_FILE,
200 fileno(ProfileFile), 0);
201 if (ProfileBuffer == MAP_FAILED) {
202 PROF_ERR("Unable to merge profile data, mmap failed: %s\n",
203 strerror(errno));
204 return -1;
205 }
206
207 if (__llvm_profile_check_compatibility(ProfileBuffer, ProfileFileSize)) {
208 (void)munmap(ProfileBuffer, ProfileFileSize);
209 PROF_WARN("Unable to merge profile data: %s\n",
210 "source profile file is not compatible.");
211 return 0;
212 }
213
214 /* Now start merging */
215 __llvm_profile_merge_from_buffer(ProfileBuffer, ProfileFileSize);
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000216
Rong Xu95ab7582018-04-02 16:57:00 +0000217 // Truncate the file in case merging of value profile did not happend to
218 // prevent from leaving garbage data at the end of the profile file.
Reid Kleckner963aba32018-04-23 17:05:47 +0000219 COMPILER_RT_FTRUNCATE(ProfileFile, __llvm_profile_get_size_for_buffer());
Rong Xu95ab7582018-04-02 16:57:00 +0000220
221 (void)munmap(ProfileBuffer, ProfileFileSize);
Xinliang David Lif50cc3e2017-06-28 16:46:06 +0000222 *MergeDone = 1;
223
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000224 return 0;
225}
226
Xinliang David Lif2d94812017-02-14 21:39:55 +0000227/* Create the directory holding the file, if needed. */
228static void createProfileDir(const char *Filename) {
229 size_t Length = strlen(Filename);
230 if (lprofFindFirstDirSeparator(Filename)) {
231 char *Copy = (char *)COMPILER_RT_ALLOCA(Length + 1);
232 strncpy(Copy, Filename, Length + 1);
233 __llvm_profile_recursive_mkdir(Copy);
234 }
235}
236
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000237/* Open the profile data for merging. It opens the file in r+b mode with
238 * file locking. If the file has content which is compatible with the
239 * current process, it also reads in the profile data in the file and merge
240 * it with in-memory counters. After the profile data is merged in memory,
241 * the original profile data is truncated and gets ready for the profile
242 * dumper. With profile merging enabled, each executable as well as any of
243 * its instrumented shared libraries dump profile data into their own data file.
244*/
Xinliang David Lif50cc3e2017-06-28 16:46:06 +0000245static FILE *openFileForMerging(const char *ProfileFileName, int *MergeDone) {
Sajjad Mirza6694b2b2019-06-24 21:32:50 +0000246 FILE *ProfileFile = NULL;
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000247 int rc;
248
Sajjad Mirza6694b2b2019-06-24 21:32:50 +0000249 ProfileFile = getProfileFile();
250 if (ProfileFile) {
251 lprofLockFileHandle(ProfileFile);
252 } else {
253 createProfileDir(ProfileFileName);
254 ProfileFile = lprofOpenFileEx(ProfileFileName);
255 }
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000256 if (!ProfileFile)
257 return NULL;
258
Xinliang David Lif50cc3e2017-06-28 16:46:06 +0000259 rc = doProfileMerging(ProfileFile, MergeDone);
260 if (rc || (!*MergeDone && COMPILER_RT_FTRUNCATE(ProfileFile, 0L)) ||
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000261 fseek(ProfileFile, 0L, SEEK_SET) == -1) {
262 PROF_ERR("Profile Merging of file %s failed: %s\n", ProfileFileName,
263 strerror(errno));
264 fclose(ProfileFile);
265 return NULL;
266 }
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000267 return ProfileFile;
268}
269
Sajjad Mirza6694b2b2019-06-24 21:32:50 +0000270static FILE *getFileObject(const char *OutputName) {
271 FILE *File;
272 File = getProfileFile();
273 if (File != NULL) {
274 return File;
275 }
276
277 return fopen(OutputName, "ab");
278}
279
Xinliang David Li315e49d2016-05-24 21:29:18 +0000280/* Write profile data to file \c OutputName. */
281static int writeFile(const char *OutputName) {
Duncan P. N. Exon Smith4543aac2014-03-21 00:27:50 +0000282 int RetVal;
Duncan P. N. Exon Smith8353a262014-03-19 22:10:27 +0000283 FILE *OutputFile;
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000284
Xinliang David Lif50cc3e2017-06-28 16:46:06 +0000285 int MergeDone = 0;
Rong Xu95ab7582018-04-02 16:57:00 +0000286 VPMergeHook = &lprofMergeValueProfData;
Sajjad Mirza6694b2b2019-06-24 21:32:50 +0000287 if (doMerging())
Hans Wennborg05d44132019-06-12 08:44:32 +0000288 OutputFile = openFileForMerging(OutputName, &MergeDone);
Sajjad Mirza6694b2b2019-06-24 21:32:50 +0000289 else
290 OutputFile = getFileObject(OutputName);
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000291
Duncan P. N. Exon Smith4543aac2014-03-21 00:27:50 +0000292 if (!OutputFile)
293 return -1;
Duncan P. N. Exon Smith8353a262014-03-19 22:10:27 +0000294
Xinliang David Li315e49d2016-05-24 21:29:18 +0000295 FreeHook = &free;
296 setupIOBuffer();
Xinliang David Li967669f2017-06-27 17:28:01 +0000297 ProfDataWriter fileWriter;
298 initFileWriter(&fileWriter, OutputFile);
Xinliang David Lif50cc3e2017-06-28 16:46:06 +0000299 RetVal = lprofWriteData(&fileWriter, lprofGetVPDataReader(), MergeDone);
Duncan P. N. Exon Smith8353a262014-03-19 22:10:27 +0000300
Sajjad Mirza6694b2b2019-06-24 21:32:50 +0000301 if (OutputFile == getProfileFile()) {
302 fflush(OutputFile);
303 if (doMerging()) {
304 lprofUnlockFileHandle(OutputFile);
305 }
306 } else {
307 fclose(OutputFile);
308 }
309
Duncan P. N. Exon Smith4543aac2014-03-21 00:27:50 +0000310 return RetVal;
Duncan P. N. Exon Smith8353a262014-03-19 22:10:27 +0000311}
312
Manman Rene73ae9a2019-03-08 15:30:56 +0000313/* Write order data to file \c OutputName. */
314static int writeOrderFile(const char *OutputName) {
315 int RetVal;
316 FILE *OutputFile;
317
318 OutputFile = fopen(OutputName, "w");
319
320 if (!OutputFile) {
321 PROF_WARN("can't open file with mode ab: %s\n", OutputName);
322 return -1;
323 }
324
325 FreeHook = &free;
326 setupIOBuffer();
327 const uint32_t *DataBegin = __llvm_profile_begin_orderfile();
328 RetVal = orderFileWriter(OutputFile, DataBegin);
329
330 fclose(OutputFile);
331 return RetVal;
332}
333
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000334static void truncateCurrentFile(void) {
Vasileios Kalintirisc9c7a3e2015-02-25 13:50:18 +0000335 const char *Filename;
Xinliang David Li315e49d2016-05-24 21:29:18 +0000336 char *FilenameBuf;
Vasileios Kalintirisc9c7a3e2015-02-25 13:50:18 +0000337 FILE *File;
Xinliang David Li315e49d2016-05-24 21:29:18 +0000338 int Length;
Vasileios Kalintirisc9c7a3e2015-02-25 13:50:18 +0000339
Xinliang David Li315e49d2016-05-24 21:29:18 +0000340 Length = getCurFilenameLength();
341 FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1);
Teresa Johnson73053b22018-07-19 19:03:50 +0000342 Filename = getCurFilename(FilenameBuf, 0);
Xinliang David Li315e49d2016-05-24 21:29:18 +0000343 if (!Filename)
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000344 return;
345
Xinliang David Lie3fc4d02016-07-21 03:38:07 +0000346 /* By pass file truncation to allow online raw profile
347 * merging. */
348 if (lprofCurFilename.MergePoolSize)
349 return;
350
Xinliang David Lif2d94812017-02-14 21:39:55 +0000351 createProfileDir(Filename);
352
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000353 /* Truncate the file. Later we'll reopen and append. */
Vasileios Kalintirisc9c7a3e2015-02-25 13:50:18 +0000354 File = fopen(Filename, "w");
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000355 if (!File)
356 return;
357 fclose(File);
358}
359
Xinliang David Li153e8b62016-06-10 20:35:01 +0000360static const char *DefaultProfileName = "default.profraw";
Xinliang David Li315e49d2016-05-24 21:29:18 +0000361static void resetFilenameToDefault(void) {
Xinliang David Li14c91c42016-08-02 19:34:00 +0000362 if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) {
363 free((void *)lprofCurFilename.FilenamePat);
364 }
Xinliang David Li7f08d122016-05-25 17:30:15 +0000365 memset(&lprofCurFilename, 0, sizeof(lprofCurFilename));
Xinliang David Li153e8b62016-06-10 20:35:01 +0000366 lprofCurFilename.FilenamePat = DefaultProfileName;
367 lprofCurFilename.PNS = PNS_default;
Xinliang David Li315e49d2016-05-24 21:29:18 +0000368}
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000369
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000370static int containsMergeSpecifier(const char *FilenamePat, int I) {
371 return (FilenamePat[I] == 'm' ||
372 (FilenamePat[I] >= '1' && FilenamePat[I] <= '9' &&
373 /* If FilenamePat[I] is not '\0', the next byte is guaranteed
374 * to be in-bound as the string is null terminated. */
375 FilenamePat[I + 1] == 'm'));
376}
Xinliang David Li7f08d122016-05-25 17:30:15 +0000377
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000378/* Parses the pattern string \p FilenamePat and stores the result to
379 * lprofcurFilename structure. */
Xinliang David Li14c91c42016-08-02 19:34:00 +0000380static int parseFilenamePattern(const char *FilenamePat,
381 unsigned CopyFilenamePat) {
Xinliang David Li315e49d2016-05-24 21:29:18 +0000382 int NumPids = 0, NumHosts = 0, I;
Xinliang David Li7f08d122016-05-25 17:30:15 +0000383 char *PidChars = &lprofCurFilename.PidChars[0];
384 char *Hostname = &lprofCurFilename.Hostname[0];
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000385 int MergingEnabled = 0;
Vasileios Kalintirisc9c7a3e2015-02-25 13:50:18 +0000386
Teresa Johnson73053b22018-07-19 19:03:50 +0000387 /* Clean up cached prefix and filename. */
Xinliang David Lib061cdb2016-07-20 05:10:56 +0000388 if (lprofCurFilename.ProfilePathPrefix)
389 free((void *)lprofCurFilename.ProfilePathPrefix);
Teresa Johnson73053b22018-07-19 19:03:50 +0000390 if (lprofCurFilename.Filename)
391 free((void *)lprofCurFilename.Filename);
392
Xinliang David Li14c91c42016-08-02 19:34:00 +0000393 if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) {
394 free((void *)lprofCurFilename.FilenamePat);
395 }
396
Igor Kudrin63600c72018-07-24 12:28:53 +0000397 memset(&lprofCurFilename, 0, sizeof(lprofCurFilename));
398
Xinliang David Li14c91c42016-08-02 19:34:00 +0000399 if (!CopyFilenamePat)
400 lprofCurFilename.FilenamePat = FilenamePat;
401 else {
402 lprofCurFilename.FilenamePat = strdup(FilenamePat);
403 lprofCurFilename.OwnsFilenamePat = 1;
404 }
Duncan P. N. Exon Smithbe0a5e12014-03-21 18:29:15 +0000405 /* Check the filename for "%p", which indicates a pid-substitution. */
Xinliang David Li315e49d2016-05-24 21:29:18 +0000406 for (I = 0; FilenamePat[I]; ++I)
407 if (FilenamePat[I] == '%') {
408 if (FilenamePat[++I] == 'p') {
Vedant Kumara06e8ca2016-01-29 23:52:11 +0000409 if (!NumPids++) {
Vedant Kumard7c93362017-12-14 19:01:04 +0000410 if (snprintf(PidChars, MAX_PID_SIZE, "%ld", (long)getpid()) <= 0) {
Bob Haarman9ee65ac2016-11-29 15:24:00 +0000411 PROF_WARN("Unable to get pid for filename pattern %s. Using the "
412 "default name.",
413 FilenamePat);
Vedant Kumara06e8ca2016-01-29 23:52:11 +0000414 return -1;
Xinliang David Li315e49d2016-05-24 21:29:18 +0000415 }
Vedant Kumara06e8ca2016-01-29 23:52:11 +0000416 }
Xinliang David Li315e49d2016-05-24 21:29:18 +0000417 } else if (FilenamePat[I] == 'h') {
Vedant Kumara06e8ca2016-01-29 23:52:11 +0000418 if (!NumHosts++)
Xinliang David Li315e49d2016-05-24 21:29:18 +0000419 if (COMPILER_RT_GETHOSTNAME(Hostname, COMPILER_RT_MAX_HOSTLEN)) {
Bob Haarman9ee65ac2016-11-29 15:24:00 +0000420 PROF_WARN("Unable to get hostname for filename pattern %s. Using "
421 "the default name.",
422 FilenamePat);
Vedant Kumara06e8ca2016-01-29 23:52:11 +0000423 return -1;
Xinliang David Li315e49d2016-05-24 21:29:18 +0000424 }
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000425 } else if (containsMergeSpecifier(FilenamePat, I)) {
426 if (MergingEnabled) {
Vedant Kumar8e2dd512016-06-14 17:23:13 +0000427 PROF_WARN("%%m specifier can only be specified once in %s.\n",
428 FilenamePat);
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000429 return -1;
430 }
431 MergingEnabled = 1;
432 if (FilenamePat[I] == 'm')
433 lprofCurFilename.MergePoolSize = 1;
434 else {
435 lprofCurFilename.MergePoolSize = FilenamePat[I] - '0';
436 I++; /* advance to 'm' */
437 }
Duncan P. N. Exon Smithe5edc882014-03-21 00:27:48 +0000438 }
Vedant Kumara06e8ca2016-01-29 23:52:11 +0000439 }
440
Xinliang David Li7f08d122016-05-25 17:30:15 +0000441 lprofCurFilename.NumPids = NumPids;
442 lprofCurFilename.NumHosts = NumHosts;
Xinliang David Li315e49d2016-05-24 21:29:18 +0000443 return 0;
444}
445
Xinliang David Li153e8b62016-06-10 20:35:01 +0000446static void parseAndSetFilename(const char *FilenamePat,
Xinliang David Li14c91c42016-08-02 19:34:00 +0000447 ProfileNameSpecifier PNS,
448 unsigned CopyFilenamePat) {
Xinliang David Li7f08d122016-05-25 17:30:15 +0000449
Xinliang David Li153e8b62016-06-10 20:35:01 +0000450 const char *OldFilenamePat = lprofCurFilename.FilenamePat;
451 ProfileNameSpecifier OldPNS = lprofCurFilename.PNS;
452
453 if (PNS < OldPNS)
454 return;
455
456 if (!FilenamePat)
457 FilenamePat = DefaultProfileName;
458
Xinliang David Li153e8b62016-06-10 20:35:01 +0000459 if (OldFilenamePat && !strcmp(OldFilenamePat, FilenamePat)) {
460 lprofCurFilename.PNS = PNS;
461 return;
462 }
463
464 /* When PNS >= OldPNS, the last one wins. */
Xinliang David Li14c91c42016-08-02 19:34:00 +0000465 if (!FilenamePat || parseFilenamePattern(FilenamePat, CopyFilenamePat))
Xinliang David Li7f08d122016-05-25 17:30:15 +0000466 resetFilenameToDefault();
Xinliang David Li153e8b62016-06-10 20:35:01 +0000467 lprofCurFilename.PNS = PNS;
Xinliang David Li7f08d122016-05-25 17:30:15 +0000468
Xinliang David Li153e8b62016-06-10 20:35:01 +0000469 if (!OldFilenamePat) {
Xinliang David Lie68df592016-09-22 21:00:29 +0000470 if (getenv("LLVM_PROFILE_VERBOSE"))
471 PROF_NOTE("Set profile file path to \"%s\" via %s.\n",
472 lprofCurFilename.FilenamePat, getPNSStr(PNS));
Xinliang David Li153e8b62016-06-10 20:35:01 +0000473 } else {
Xinliang David Lie68df592016-09-22 21:00:29 +0000474 if (getenv("LLVM_PROFILE_VERBOSE"))
475 PROF_NOTE("Override old profile path \"%s\" via %s to \"%s\" via %s.\n",
476 OldFilenamePat, getPNSStr(OldPNS), lprofCurFilename.FilenamePat,
477 getPNSStr(PNS));
Xinliang David Li153e8b62016-06-10 20:35:01 +0000478 }
Xinliang David Li7f08d122016-05-25 17:30:15 +0000479
Xinliang David Lie3fc4d02016-07-21 03:38:07 +0000480 truncateCurrentFile();
Xinliang David Li7f08d122016-05-25 17:30:15 +0000481}
482
Xinliang David Li315e49d2016-05-24 21:29:18 +0000483/* Return buffer length that is required to store the current profile
484 * filename with PID and hostname substitutions. */
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000485/* The length to hold uint64_t followed by 2 digit pool id including '_' */
486#define SIGLEN 24
Xinliang David Li315e49d2016-05-24 21:29:18 +0000487static int getCurFilenameLength() {
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000488 int Len;
Xinliang David Li315e49d2016-05-24 21:29:18 +0000489 if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0])
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000490 return 0;
Duncan P. N. Exon Smithe5edc882014-03-21 00:27:48 +0000491
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000492 if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts ||
493 lprofCurFilename.MergePoolSize))
Xinliang David Li315e49d2016-05-24 21:29:18 +0000494 return strlen(lprofCurFilename.FilenamePat);
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000495
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000496 Len = strlen(lprofCurFilename.FilenamePat) +
497 lprofCurFilename.NumPids * (strlen(lprofCurFilename.PidChars) - 2) +
498 lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2);
499 if (lprofCurFilename.MergePoolSize)
500 Len += SIGLEN;
501 return Len;
Xinliang David Li315e49d2016-05-24 21:29:18 +0000502}
503
504/* Return the pointer to the current profile file name (after substituting
505 * PIDs and Hostnames in filename pattern. \p FilenameBuf is the buffer
506 * to store the resulting filename. If no substitution is needed, the
Teresa Johnson73053b22018-07-19 19:03:50 +0000507 * current filename pattern string is directly returned, unless ForceUseBuf
508 * is enabled. */
509static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf) {
510 int I, J, PidLength, HostNameLength, FilenamePatLength;
Xinliang David Li315e49d2016-05-24 21:29:18 +0000511 const char *FilenamePat = lprofCurFilename.FilenamePat;
512
513 if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0])
514 return 0;
515
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000516 if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts ||
Teresa Johnson73053b22018-07-19 19:03:50 +0000517 lprofCurFilename.MergePoolSize)) {
518 if (!ForceUseBuf)
519 return lprofCurFilename.FilenamePat;
520
521 FilenamePatLength = strlen(lprofCurFilename.FilenamePat);
522 memcpy(FilenameBuf, lprofCurFilename.FilenamePat, FilenamePatLength);
523 FilenameBuf[FilenamePatLength] = '\0';
524 return FilenameBuf;
525 }
Xinliang David Li315e49d2016-05-24 21:29:18 +0000526
527 PidLength = strlen(lprofCurFilename.PidChars);
528 HostNameLength = strlen(lprofCurFilename.Hostname);
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000529 /* Construct the new filename. */
Xinliang David Li315e49d2016-05-24 21:29:18 +0000530 for (I = 0, J = 0; FilenamePat[I]; ++I)
531 if (FilenamePat[I] == '%') {
532 if (FilenamePat[++I] == 'p') {
533 memcpy(FilenameBuf + J, lprofCurFilename.PidChars, PidLength);
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000534 J += PidLength;
Xinliang David Li315e49d2016-05-24 21:29:18 +0000535 } else if (FilenamePat[I] == 'h') {
536 memcpy(FilenameBuf + J, lprofCurFilename.Hostname, HostNameLength);
Vedant Kumara06e8ca2016-01-29 23:52:11 +0000537 J += HostNameLength;
Xinliang David Lie2ce2e02016-06-08 23:43:56 +0000538 } else if (containsMergeSpecifier(FilenamePat, I)) {
539 char LoadModuleSignature[SIGLEN];
540 int S;
541 int ProfilePoolId = getpid() % lprofCurFilename.MergePoolSize;
542 S = snprintf(LoadModuleSignature, SIGLEN, "%" PRIu64 "_%d",
543 lprofGetLoadModuleSignature(), ProfilePoolId);
544 if (S == -1 || S > SIGLEN)
545 S = SIGLEN;
546 memcpy(FilenameBuf + J, LoadModuleSignature, S);
547 J += S;
548 if (FilenamePat[I] != 'm')
549 I++;
Vedant Kumara06e8ca2016-01-29 23:52:11 +0000550 }
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000551 /* Drop any unknown substitutions. */
552 } else
Xinliang David Li315e49d2016-05-24 21:29:18 +0000553 FilenameBuf[J++] = FilenamePat[I];
554 FilenameBuf[J] = 0;
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000555
Xinliang David Li315e49d2016-05-24 21:29:18 +0000556 return FilenameBuf;
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000557}
558
Xinliang David Li315e49d2016-05-24 21:29:18 +0000559/* Returns the pointer to the environment variable
560 * string. Returns null if the env var is not set. */
561static const char *getFilenamePatFromEnv(void) {
Eric Christopherd641b532015-04-28 22:54:51 +0000562 const char *Filename = getenv("LLVM_PROFILE_FILE");
Eric Christopherd641b532015-04-28 22:54:51 +0000563 if (!Filename || !Filename[0])
Xinliang David Li51fe0022016-05-24 01:23:09 +0000564 return 0;
565 return Filename;
Eric Christopherd641b532015-04-28 22:54:51 +0000566}
567
Xinliang David Lieaf238d2016-07-20 04:26:09 +0000568COMPILER_RT_VISIBILITY
569const char *__llvm_profile_get_path_prefix(void) {
570 int Length;
571 char *FilenameBuf, *Prefix;
572 const char *Filename, *PrefixEnd;
573
574 if (lprofCurFilename.ProfilePathPrefix)
575 return lprofCurFilename.ProfilePathPrefix;
576
577 Length = getCurFilenameLength();
578 FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1);
Teresa Johnson73053b22018-07-19 19:03:50 +0000579 Filename = getCurFilename(FilenameBuf, 0);
Xinliang David Lieaf238d2016-07-20 04:26:09 +0000580 if (!Filename)
581 return "\0";
582
583 PrefixEnd = lprofFindLastDirSeparator(Filename);
584 if (!PrefixEnd)
585 return "\0";
586
587 Length = PrefixEnd - Filename + 1;
588 Prefix = (char *)malloc(Length + 1);
589 if (!Prefix) {
590 PROF_ERR("Failed to %s\n", "allocate memory.");
591 return "\0";
592 }
593 memcpy(Prefix, Filename, Length);
594 Prefix[Length] = '\0';
595 lprofCurFilename.ProfilePathPrefix = Prefix;
596 return Prefix;
597}
598
Teresa Johnson73053b22018-07-19 19:03:50 +0000599COMPILER_RT_VISIBILITY
600const char *__llvm_profile_get_filename(void) {
601 int Length;
602 char *FilenameBuf;
603 const char *Filename;
604
605 if (lprofCurFilename.Filename)
606 return lprofCurFilename.Filename;
607
608 Length = getCurFilenameLength();
609 FilenameBuf = (char *)malloc(Length + 1);
610 if (!FilenameBuf) {
611 PROF_ERR("Failed to %s\n", "allocate memory.");
612 return "\0";
613 }
614 Filename = getCurFilename(FilenameBuf, 1);
615 if (!Filename)
616 return "\0";
617
618 lprofCurFilename.Filename = FilenameBuf;
619 return FilenameBuf;
620}
621
Xinliang David Li51fe0022016-05-24 01:23:09 +0000622/* This method is invoked by the runtime initialization hook
623 * InstrProfilingRuntime.o if it is linked in. Both user specified
624 * profile path via -fprofile-instr-generate= and LLVM_PROFILE_FILE
625 * environment variable can override this default value. */
Xinliang David Liabfd5532015-12-16 03:29:15 +0000626COMPILER_RT_VISIBILITY
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000627void __llvm_profile_initialize_file(void) {
Xinliang David Lie9539332016-07-21 23:19:18 +0000628 const char *EnvFilenamePat;
Xinliang David Li1c9320c2017-08-15 03:13:01 +0000629 const char *SelectedPat = NULL;
630 ProfileNameSpecifier PNS = PNS_unknown;
Xinliang David Lie9539332016-07-21 23:19:18 +0000631 int hasCommandLineOverrider = (INSTR_PROF_PROFILE_NAME_VAR[0] != 0);
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000632
Xinliang David Lie9539332016-07-21 23:19:18 +0000633 EnvFilenamePat = getFilenamePatFromEnv();
Xinliang David Li1c9320c2017-08-15 03:13:01 +0000634 if (EnvFilenamePat) {
Sajjad Mirza6694b2b2019-06-24 21:32:50 +0000635 /* Pass CopyFilenamePat = 1, to ensure that the filename would be valid
Xinliang David Lic7c53032017-08-23 21:39:33 +0000636 at the moment when __llvm_profile_write_file() gets executed. */
637 parseAndSetFilename(EnvFilenamePat, PNS_environment, 1);
638 return;
Xinliang David Li1c9320c2017-08-15 03:13:01 +0000639 } else if (hasCommandLineOverrider) {
640 SelectedPat = INSTR_PROF_PROFILE_NAME_VAR;
641 PNS = PNS_command_line;
Xinliang David Lie9539332016-07-21 23:19:18 +0000642 } else {
Xinliang David Li1c9320c2017-08-15 03:13:01 +0000643 SelectedPat = NULL;
644 PNS = PNS_default;
Xinliang David Lie9539332016-07-21 23:19:18 +0000645 }
646
Xinliang David Li1c9320c2017-08-15 03:13:01 +0000647 parseAndSetFilename(SelectedPat, PNS, 0);
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000648}
649
Xinliang David Li51fe0022016-05-24 01:23:09 +0000650/* This API is directly called by the user application code. It has the
651 * highest precedence compared with LLVM_PROFILE_FILE environment variable
Xinliang David Li41518942016-05-24 02:37:07 +0000652 * and command line option -fprofile-instr-generate=<profile_name>.
Xinliang David Li51fe0022016-05-24 01:23:09 +0000653 */
Xinliang David Liabfd5532015-12-16 03:29:15 +0000654COMPILER_RT_VISIBILITY
Xinliang David Li315e49d2016-05-24 21:29:18 +0000655void __llvm_profile_set_filename(const char *FilenamePat) {
Xinliang David Li14c91c42016-08-02 19:34:00 +0000656 parseAndSetFilename(FilenamePat, PNS_runtime_api, 1);
Eric Christopherd641b532015-04-28 22:54:51 +0000657}
658
Xinliang David Li315e49d2016-05-24 21:29:18 +0000659/* The public API for writing profile data into the file with name
660 * set by previous calls to __llvm_profile_set_filename or
661 * __llvm_profile_override_default_filename or
662 * __llvm_profile_initialize_file. */
Xinliang David Liabfd5532015-12-16 03:29:15 +0000663COMPILER_RT_VISIBILITY
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000664int __llvm_profile_write_file(void) {
Xinliang David Li315e49d2016-05-24 21:29:18 +0000665 int rc, Length;
666 const char *Filename;
667 char *FilenameBuf;
Rong Xucf1f6fb2017-03-17 18:41:33 +0000668 int PDeathSig = 0;
Vasileios Kalintirisc9c7a3e2015-02-25 13:50:18 +0000669
Xinliang David Li3b2c0022016-08-09 04:21:14 +0000670 if (lprofProfileDumped()) {
Sajjad Mirza6694b2b2019-06-24 21:32:50 +0000671 PROF_NOTE("Profile data not written to file: %s.\n", "already written");
Xinliang David Li3b2c0022016-08-09 04:21:14 +0000672 return 0;
673 }
674
Xinliang David Li315e49d2016-05-24 21:29:18 +0000675 Length = getCurFilenameLength();
676 FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1);
Teresa Johnson73053b22018-07-19 19:03:50 +0000677 Filename = getCurFilename(FilenameBuf, 0);
Xinliang David Li315e49d2016-05-24 21:29:18 +0000678
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000679 /* Check the filename. */
Xinliang David Li315e49d2016-05-24 21:29:18 +0000680 if (!Filename) {
Xinliang David Li690c31f2016-05-20 05:15:42 +0000681 PROF_ERR("Failed to write file : %s\n", "Filename not set");
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000682 return -1;
Xinliang David Li9ea30642015-12-03 18:31:59 +0000683 }
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000684
Xinliang David Lid85c32c2016-01-08 23:42:28 +0000685 /* Check if there is llvm/runtime version mismatch. */
Xinliang David Lia6924212016-01-08 23:31:57 +0000686 if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) {
Xinliang David Li690c31f2016-05-20 05:15:42 +0000687 PROF_ERR("Runtime and instrumentation version mismatch : "
Xinliang David Lia6924212016-01-08 23:31:57 +0000688 "expected %d, but get %d\n",
689 INSTR_PROF_RAW_VERSION,
690 (int)GET_VERSION(__llvm_profile_get_version()));
691 return -1;
692 }
693
Rong Xucf1f6fb2017-03-17 18:41:33 +0000694 // Temporarily suspend getting SIGKILL when the parent exits.
695 PDeathSig = lprofSuspendSigKill();
696
Xinliang David Li315e49d2016-05-24 21:29:18 +0000697 /* Write profile data to the file. */
698 rc = writeFile(Filename);
Xinliang David Li9ea30642015-12-03 18:31:59 +0000699 if (rc)
Xinliang David Li315e49d2016-05-24 21:29:18 +0000700 PROF_ERR("Failed to write file \"%s\": %s\n", Filename, strerror(errno));
Rong Xucf1f6fb2017-03-17 18:41:33 +0000701
702 // Restore SIGKILL.
703 if (PDeathSig == 1)
704 lprofRestoreSigKill();
705
Justin Bogner66fd5c92015-01-16 20:10:56 +0000706 return rc;
Duncan P. N. Exon Smith54cc0e22014-03-20 19:23:55 +0000707}
708
Xinliang David Li3b2c0022016-08-09 04:21:14 +0000709COMPILER_RT_VISIBILITY
710int __llvm_profile_dump(void) {
711 if (!doMerging())
712 PROF_WARN("Later invocation of __llvm_profile_dump can lead to clobbering "
Nico Weber81291a02016-09-08 01:46:52 +0000713 " of previously dumped profile data : %s. Either use %%m "
Xinliang David Li3b2c0022016-08-09 04:21:14 +0000714 "in profile name or change profile name before dumping.\n",
715 "online profile merging is not on");
716 int rc = __llvm_profile_write_file();
717 lprofSetProfileDumped();
718 return rc;
719}
720
Manman Rene73ae9a2019-03-08 15:30:56 +0000721/* Order file data will be saved in a file with suffx .order. */
722static const char *OrderFileSuffix = ".order";
723
724COMPILER_RT_VISIBILITY
725int __llvm_orderfile_write_file(void) {
726 int rc, Length, LengthBeforeAppend, SuffixLength;
727 const char *Filename;
728 char *FilenameBuf;
729 int PDeathSig = 0;
730
731 SuffixLength = strlen(OrderFileSuffix);
732 Length = getCurFilenameLength() + SuffixLength;
733 FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1);
734 Filename = getCurFilename(FilenameBuf, 1);
735
736 /* Check the filename. */
737 if (!Filename) {
738 PROF_ERR("Failed to write file : %s\n", "Filename not set");
739 return -1;
740 }
741
742 /* Append order file suffix */
743 LengthBeforeAppend = strlen(Filename);
744 memcpy(FilenameBuf + LengthBeforeAppend, OrderFileSuffix, SuffixLength);
745 FilenameBuf[LengthBeforeAppend + SuffixLength] = '\0';
746
747 /* Check if there is llvm/runtime version mismatch. */
748 if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) {
749 PROF_ERR("Runtime and instrumentation version mismatch : "
750 "expected %d, but get %d\n",
751 INSTR_PROF_RAW_VERSION,
752 (int)GET_VERSION(__llvm_profile_get_version()));
753 return -1;
754 }
755
756 // Temporarily suspend getting SIGKILL when the parent exits.
757 PDeathSig = lprofSuspendSigKill();
758
759 /* Write order data to the file. */
760 rc = writeOrderFile(Filename);
761 if (rc)
762 PROF_ERR("Failed to write file \"%s\": %s\n", Filename, strerror(errno));
763
764 // Restore SIGKILL.
765 if (PDeathSig == 1)
766 lprofRestoreSigKill();
767
768 return rc;
769}
770
771COMPILER_RT_VISIBILITY
772int __llvm_orderfile_dump(void) {
773 int rc = __llvm_orderfile_write_file();
774 return rc;
775}
776
Xinliang David Li6fe18f42015-11-23 04:38:17 +0000777static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); }
Duncan P. N. Exon Smithbe0a5e12014-03-21 18:29:15 +0000778
Xinliang David Liabfd5532015-12-16 03:29:15 +0000779COMPILER_RT_VISIBILITY
Duncan P. N. Exon Smith55e4d662014-05-17 01:27:30 +0000780int __llvm_profile_register_write_file_atexit(void) {
Duncan P. N. Exon Smith8353a262014-03-19 22:10:27 +0000781 static int HasBeenRegistered = 0;
782
Duncan P. N. Exon Smithbe0a5e12014-03-21 18:29:15 +0000783 if (HasBeenRegistered)
784 return 0;
785
Xinliang David Li4617aa72016-05-18 22:34:05 +0000786 lprofSetupValueProfiler();
787
Duncan P. N. Exon Smithbe0a5e12014-03-21 18:29:15 +0000788 HasBeenRegistered = 1;
789 return atexit(writeFileWithoutReturn);
Duncan P. N. Exon Smith8353a262014-03-19 22:10:27 +0000790}
Petr Hosek47e5fcb2018-07-25 03:01:35 +0000791
792#endif