• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.  */
fileWriter(ProfDataIOVec * IOVecs,uint32_t NumIOVecs,void ** WriterCtx)21 static 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 
writeFile(FILE * File)33 static int writeFile(FILE *File) {
34   uint8_t *ValueDataBegin = NULL;
35   const uint64_t ValueDataSize =
36       __llvm_profile_gather_value_data(&ValueDataBegin);
37   int r = llvmWriteProfData(fileWriter, File, ValueDataBegin, ValueDataSize);
38   free(ValueDataBegin);
39   return r;
40 }
41 
writeFileWithName(const char * OutputName)42 static int writeFileWithName(const char *OutputName) {
43   int RetVal;
44   FILE *OutputFile;
45   if (!OutputName || !OutputName[0])
46     return -1;
47 
48   /* Append to the file to support profiling multiple shared objects. */
49   OutputFile = fopen(OutputName, "ab");
50   if (!OutputFile)
51     return -1;
52 
53   RetVal = writeFile(OutputFile);
54 
55   fclose(OutputFile);
56   return RetVal;
57 }
58 
59 COMPILER_RT_WEAK int __llvm_profile_OwnsFilename = 0;
60 COMPILER_RT_WEAK const char *__llvm_profile_CurrentFilename = NULL;
61 
truncateCurrentFile(void)62 static void truncateCurrentFile(void) {
63   const char *Filename;
64   FILE *File;
65 
66   Filename = __llvm_profile_CurrentFilename;
67   if (!Filename || !Filename[0])
68     return;
69 
70   /* Create the directory holding the file, if needed. */
71   if (strchr(Filename, '/')) {
72     char *Copy = malloc(strlen(Filename) + 1);
73     strcpy(Copy, Filename);
74     __llvm_profile_recursive_mkdir(Copy);
75     free(Copy);
76   }
77 
78   /* Truncate the file.  Later we'll reopen and append. */
79   File = fopen(Filename, "w");
80   if (!File)
81     return;
82   fclose(File);
83 }
84 
setFilename(const char * Filename,int OwnsFilename)85 static void setFilename(const char *Filename, int OwnsFilename) {
86   /* Check if this is a new filename and therefore needs truncation. */
87   int NewFile = !__llvm_profile_CurrentFilename ||
88       (Filename && strcmp(Filename, __llvm_profile_CurrentFilename));
89   if (__llvm_profile_OwnsFilename)
90     free(UNCONST(__llvm_profile_CurrentFilename));
91 
92   __llvm_profile_CurrentFilename = Filename;
93   __llvm_profile_OwnsFilename = OwnsFilename;
94 
95   /* If not a new file, append to support profiling multiple shared objects. */
96   if (NewFile)
97     truncateCurrentFile();
98 }
99 
resetFilenameToDefault(void)100 static void resetFilenameToDefault(void) { setFilename("default.profraw", 0); }
101 
102 int getpid(void);
setFilenamePossiblyWithPid(const char * Filename)103 static int setFilenamePossiblyWithPid(const char *Filename) {
104 #define MAX_PID_SIZE 16
105   char PidChars[MAX_PID_SIZE] = {0};
106   int NumPids = 0, PidLength = 0;
107   char *Allocated;
108   int I, J;
109 
110   /* Reset filename on NULL, except with env var which is checked by caller. */
111   if (!Filename) {
112     resetFilenameToDefault();
113     return 0;
114   }
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 
setFilenameFromEnvironment(void)151 static int setFilenameFromEnvironment(void) {
152   const char *Filename = getenv("LLVM_PROFILE_FILE");
153 
154   if (!Filename || !Filename[0])
155     return -1;
156 
157   return setFilenamePossiblyWithPid(Filename);
158 }
159 
setFilenameAutomatically(void)160 static void setFilenameAutomatically(void) {
161   if (!setFilenameFromEnvironment())
162     return;
163 
164   resetFilenameToDefault();
165 }
166 
167 COMPILER_RT_VISIBILITY
__llvm_profile_initialize_file(void)168 void __llvm_profile_initialize_file(void) {
169   /* Check if the filename has been initialized. */
170   if (__llvm_profile_CurrentFilename)
171     return;
172 
173   /* Detect the filename and truncate. */
174   setFilenameAutomatically();
175 }
176 
177 COMPILER_RT_VISIBILITY
__llvm_profile_set_filename(const char * Filename)178 void __llvm_profile_set_filename(const char *Filename) {
179   setFilenamePossiblyWithPid(Filename);
180 }
181 
182 COMPILER_RT_VISIBILITY
__llvm_profile_override_default_filename(const char * Filename)183 void __llvm_profile_override_default_filename(const char *Filename) {
184   /* If the env var is set, skip setting filename from argument. */
185   const char *Env_Filename = getenv("LLVM_PROFILE_FILE");
186   if (Env_Filename && Env_Filename[0])
187     return;
188   setFilenamePossiblyWithPid(Filename);
189 }
190 
191 COMPILER_RT_VISIBILITY
__llvm_profile_write_file(void)192 int __llvm_profile_write_file(void) {
193   int rc;
194 
195   GetEnvHook = &getenv;
196   /* Check the filename. */
197   if (!__llvm_profile_CurrentFilename) {
198     PROF_ERR("LLVM Profile: Failed to write file : %s\n", "Filename not set");
199     return -1;
200   }
201 
202   /* Write the file. */
203   rc = writeFileWithName(__llvm_profile_CurrentFilename);
204   if (rc)
205     PROF_ERR("LLVM Profile: Failed to write file \"%s\": %s\n",
206             __llvm_profile_CurrentFilename, strerror(errno));
207   return rc;
208 }
209 
writeFileWithoutReturn(void)210 static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); }
211 
212 COMPILER_RT_VISIBILITY
__llvm_profile_register_write_file_atexit(void)213 int __llvm_profile_register_write_file_atexit(void) {
214   static int HasBeenRegistered = 0;
215 
216   if (HasBeenRegistered)
217     return 0;
218 
219   HasBeenRegistered = 1;
220   return atexit(writeFileWithoutReturn);
221 }
222