1 /*===- InstrProfilingUtil.c - Support library for PGO instrumentation -----===*\
2 |*
3 |* 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
6 |*
7 \*===----------------------------------------------------------------------===*/
8 
9 #ifdef _WIN32
10 #include <direct.h>
11 #include <process.h>
12 #include <windows.h>
13 #include "WindowsMMap.h"
14 #else
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <unistd.h>
18 #include <fcntl.h>
19 #include <errno.h>
20 #endif
21 
22 #ifdef COMPILER_RT_HAS_UNAME
23 #include <sys/utsname.h>
24 #endif
25 
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #if defined(__linux__)
30 #include <signal.h>
31 #include <sys/prctl.h>
32 #endif
33 
34 #include "InstrProfiling.h"
35 #include "InstrProfilingUtil.h"
36 
37 COMPILER_RT_WEAK unsigned lprofDirMode = 0755;
38 
39 COMPILER_RT_VISIBILITY
40 void __llvm_profile_recursive_mkdir(char *path) {
41   int i;
42 
43   for (i = 1; path[i] != '\0'; ++i) {
44     char save = path[i];
45     if (!IS_DIR_SEPARATOR(path[i]))
46       continue;
47     path[i] = '\0';
48 #ifdef _WIN32
49     _mkdir(path);
50 #else
51     /* Some of these will fail, ignore it. */
52     mkdir(path, __llvm_profile_get_dir_mode());
53 #endif
54     path[i] = save;
55   }
56 }
57 
58 COMPILER_RT_VISIBILITY
59 void __llvm_profile_set_dir_mode(unsigned Mode) { lprofDirMode = Mode; }
60 
61 COMPILER_RT_VISIBILITY
62 unsigned __llvm_profile_get_dir_mode(void) { return lprofDirMode; }
63 
64 #if COMPILER_RT_HAS_ATOMICS != 1
65 COMPILER_RT_VISIBILITY
66 uint32_t lprofBoolCmpXchg(void **Ptr, void *OldV, void *NewV) {
67   void *R = *Ptr;
68   if (R == OldV) {
69     *Ptr = NewV;
70     return 1;
71   }
72   return 0;
73 }
74 COMPILER_RT_VISIBILITY
75 void *lprofPtrFetchAdd(void **Mem, long ByteIncr) {
76   void *Old = *Mem;
77   *((char **)Mem) += ByteIncr;
78   return Old;
79 }
80 
81 #endif
82 
83 #ifdef _WIN32
84 COMPILER_RT_VISIBILITY int lprofGetHostName(char *Name, int Len) {
85   WCHAR Buffer[COMPILER_RT_MAX_HOSTLEN];
86   DWORD BufferSize = sizeof(Buffer);
87   BOOL Result =
88       GetComputerNameExW(ComputerNameDnsFullyQualified, Buffer, &BufferSize);
89   if (!Result)
90     return -1;
91   if (WideCharToMultiByte(CP_UTF8, 0, Buffer, -1, Name, Len, NULL, NULL) == 0)
92     return -1;
93   return 0;
94 }
95 #elif defined(COMPILER_RT_HAS_UNAME)
96 COMPILER_RT_VISIBILITY int lprofGetHostName(char *Name, int Len) {
97   struct utsname N;
98   int R = uname(&N);
99   if (R >= 0) {
100     strncpy(Name, N.nodename, Len);
101     return 0;
102   }
103   return R;
104 }
105 #endif
106 
107 COMPILER_RT_VISIBILITY int lprofLockFd(int fd) {
108 #ifdef COMPILER_RT_HAS_FCNTL_LCK
109   struct flock s_flock;
110 
111   s_flock.l_whence = SEEK_SET;
112   s_flock.l_start = 0;
113   s_flock.l_len = 0; /* Until EOF.  */
114   s_flock.l_pid = getpid();
115   s_flock.l_type = F_WRLCK;
116 
117   while (fcntl(fd, F_SETLKW, &s_flock) == -1) {
118     if (errno != EINTR) {
119       if (errno == ENOLCK) {
120         return -1;
121       }
122       break;
123     }
124   }
125   return 0;
126 #else
127   flock(fd, LOCK_EX);
128   return 0;
129 #endif
130 }
131 
132 COMPILER_RT_VISIBILITY int lprofUnlockFd(int fd) {
133 #ifdef COMPILER_RT_HAS_FCNTL_LCK
134   struct flock s_flock;
135 
136   s_flock.l_whence = SEEK_SET;
137   s_flock.l_start = 0;
138   s_flock.l_len = 0; /* Until EOF.  */
139   s_flock.l_pid = getpid();
140   s_flock.l_type = F_UNLCK;
141 
142   while (fcntl(fd, F_SETLKW, &s_flock) == -1) {
143     if (errno != EINTR) {
144       if (errno == ENOLCK) {
145         return -1;
146       }
147       break;
148     }
149   }
150   return 0;
151 #else
152   flock(fd, LOCK_UN);
153   return 0;
154 #endif
155 }
156 
157 COMPILER_RT_VISIBILITY FILE *lprofOpenFileEx(const char *ProfileName) {
158   FILE *f;
159   int fd;
160 #ifdef COMPILER_RT_HAS_FCNTL_LCK
161   fd = open(ProfileName, O_RDWR | O_CREAT, 0666);
162   if (fd < 0)
163     return NULL;
164 
165   if (lprofLockFd(fd) != 0)
166     PROF_WARN("Data may be corrupted during profile merging : %s\n",
167               "Fail to obtain file lock due to system limit.");
168 
169   f = fdopen(fd, "r+b");
170 #elif defined(_WIN32)
171   // FIXME: Use the wide variants to handle Unicode filenames.
172   HANDLE h = CreateFileA(ProfileName, GENERIC_READ | GENERIC_WRITE, 0, 0,
173                          OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
174   if (h == INVALID_HANDLE_VALUE)
175     return NULL;
176 
177   fd = _open_osfhandle((intptr_t)h, 0);
178   if (fd == -1) {
179     CloseHandle(h);
180     return NULL;
181   }
182 
183   f = _fdopen(fd, "r+b");
184   if (f == 0) {
185     CloseHandle(h);
186     return NULL;
187   }
188 #else
189   /* Worst case no locking applied.  */
190   PROF_WARN("Concurrent file access is not supported : %s\n",
191             "lack file locking");
192   fd = open(ProfileName, O_RDWR | O_CREAT, 0666);
193   if (fd < 0)
194     return NULL;
195   f = fdopen(fd, "r+b");
196 #endif
197 
198   return f;
199 }
200 
201 COMPILER_RT_VISIBILITY const char *lprofGetPathPrefix(int *PrefixStrip,
202                                                       size_t *PrefixLen) {
203   const char *Prefix = getenv("GCOV_PREFIX");
204   const char *PrefixStripStr = getenv("GCOV_PREFIX_STRIP");
205 
206   *PrefixLen = 0;
207   *PrefixStrip = 0;
208   if (Prefix == NULL || Prefix[0] == '\0')
209     return NULL;
210 
211   if (PrefixStripStr) {
212     *PrefixStrip = atoi(PrefixStripStr);
213 
214     /* Negative GCOV_PREFIX_STRIP values are ignored */
215     if (*PrefixStrip < 0)
216       *PrefixStrip = 0;
217   } else {
218     *PrefixStrip = 0;
219   }
220   *PrefixLen = strlen(Prefix);
221 
222   return Prefix;
223 }
224 
225 COMPILER_RT_VISIBILITY void
226 lprofApplyPathPrefix(char *Dest, const char *PathStr, const char *Prefix,
227                      size_t PrefixLen, int PrefixStrip) {
228 
229   const char *Ptr;
230   int Level;
231   const char *StrippedPathStr = PathStr;
232 
233   for (Level = 0, Ptr = PathStr + 1; Level < PrefixStrip; ++Ptr) {
234     if (*Ptr == '\0')
235       break;
236 
237     if (!IS_DIR_SEPARATOR(*Ptr))
238       continue;
239 
240     StrippedPathStr = Ptr;
241     ++Level;
242   }
243 
244   memcpy(Dest, Prefix, PrefixLen);
245 
246   if (!IS_DIR_SEPARATOR(Prefix[PrefixLen - 1]))
247     Dest[PrefixLen++] = DIR_SEPARATOR;
248 
249   memcpy(Dest + PrefixLen, StrippedPathStr, strlen(StrippedPathStr) + 1);
250 }
251 
252 COMPILER_RT_VISIBILITY const char *
253 lprofFindFirstDirSeparator(const char *Path) {
254   const char *Sep = strchr(Path, DIR_SEPARATOR);
255 #if defined(DIR_SEPARATOR_2)
256   const char *Sep2 = strchr(Path, DIR_SEPARATOR_2);
257   if (Sep2 && (!Sep || Sep2 < Sep))
258     Sep = Sep2;
259 #endif
260   return Sep;
261 }
262 
263 COMPILER_RT_VISIBILITY const char *lprofFindLastDirSeparator(const char *Path) {
264   const char *Sep = strrchr(Path, DIR_SEPARATOR);
265 #if defined(DIR_SEPARATOR_2)
266   const char *Sep2 = strrchr(Path, DIR_SEPARATOR_2);
267   if (Sep2 && (!Sep || Sep2 > Sep))
268     Sep = Sep2;
269 #endif
270   return Sep;
271 }
272 
273 COMPILER_RT_VISIBILITY int lprofSuspendSigKill() {
274 #if defined(__linux__)
275   int PDeachSig = 0;
276   /* Temporarily suspend getting SIGKILL upon exit of the parent process. */
277   if (prctl(PR_GET_PDEATHSIG, &PDeachSig) == 0 && PDeachSig == SIGKILL)
278     prctl(PR_SET_PDEATHSIG, 0);
279   return (PDeachSig == SIGKILL);
280 #else
281   return 0;
282 #endif
283 }
284 
285 COMPILER_RT_VISIBILITY void lprofRestoreSigKill() {
286 #if defined(__linux__)
287   prctl(PR_SET_PDEATHSIG, SIGKILL);
288 #endif
289 }
290