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 <errno.h>
16#include <fcntl.h>
17#include <sys/file.h>
18#include <sys/mman.h>
19#include <sys/stat.h>
20#include <sys/types.h>
21#include <unistd.h>
22#endif
23
24#ifdef COMPILER_RT_HAS_UNAME
25#include <sys/utsname.h>
26#endif
27
28#include <stdlib.h>
29#include <string.h>
30
31#if defined(__linux__)
32#include <signal.h>
33#include <sys/prctl.h>
34#endif
35
36#if defined(__Fuchsia__)
37#include <zircon/process.h>
38#include <zircon/syscalls.h>
39#endif
40
41#if defined(__FreeBSD__)
42#include <signal.h>
43#include <sys/procctl.h>
44#endif
45
46#include "InstrProfiling.h"
47#include "InstrProfilingUtil.h"
48
49COMPILER_RT_VISIBILITY unsigned lprofDirMode = 0755;
50
51COMPILER_RT_VISIBILITY
52void __llvm_profile_recursive_mkdir(char *path) {
53 int i;
54 int start = 1;
55
56#if defined(__ANDROID__) && defined(__ANDROID_API__) && \
57 defined(__ANDROID_API_FUTURE__) && \
58 __ANDROID_API__ == __ANDROID_API_FUTURE__
59 // Avoid spammy selinux denial messages in Android by not attempting to
60 // create directories in GCOV_PREFIX. These denials occur when creating (or
61 // even attempting to stat()) top-level directories like "/data".
62 //
63 // Do so by ignoring ${GCOV_PREFIX} when invoking mkdir().
64 const char *gcov_prefix = getenv("GCOV_PREFIX");
65 if (gcov_prefix != NULL) {
66 const int gcov_prefix_len = strlen(gcov_prefix);
67 if (strncmp(path, gcov_prefix, gcov_prefix_len) == 0)
68 start = gcov_prefix_len;
69 }
70#endif
71
72 for (i = start; path[i] != '\0'; ++i) {
73 char save = path[i];
74 if (!IS_DIR_SEPARATOR(path[i]))
75 continue;
76 path[i] = '\0';
77#ifdef _WIN32
78 _mkdir(path);
79#else
80 /* Some of these will fail, ignore it. */
81 mkdir(path: path, mode: __llvm_profile_get_dir_mode());
82#endif
83 path[i] = save;
84 }
85}
86
87COMPILER_RT_VISIBILITY
88void __llvm_profile_set_dir_mode(unsigned Mode) { lprofDirMode = Mode; }
89
90COMPILER_RT_VISIBILITY
91unsigned __llvm_profile_get_dir_mode(void) { return lprofDirMode; }
92
93#if COMPILER_RT_HAS_ATOMICS != 1
94COMPILER_RT_VISIBILITY
95uint32_t lprofBoolCmpXchg(void **Ptr, void *OldV, void *NewV) {
96 void *R = *Ptr;
97 if (R == OldV) {
98 *Ptr = NewV;
99 return 1;
100 }
101 return 0;
102}
103COMPILER_RT_VISIBILITY
104void *lprofPtrFetchAdd(void **Mem, long ByteIncr) {
105 void *Old = *Mem;
106 *((char **)Mem) += ByteIncr;
107 return Old;
108}
109
110#endif
111
112#ifdef _WIN32
113COMPILER_RT_VISIBILITY int lprofGetHostName(char *Name, int Len) {
114 WCHAR Buffer[COMPILER_RT_MAX_HOSTLEN];
115 DWORD BufferSize = sizeof(Buffer);
116 BOOL Result =
117 GetComputerNameExW(ComputerNameDnsFullyQualified, Buffer, &BufferSize);
118 if (!Result)
119 return -1;
120 if (WideCharToMultiByte(CP_UTF8, 0, Buffer, -1, Name, Len, NULL, NULL) == 0)
121 return -1;
122 return 0;
123}
124#elif defined(COMPILER_RT_HAS_UNAME)
125COMPILER_RT_VISIBILITY int lprofGetHostName(char *Name, int Len) {
126 struct utsname N;
127 int R = uname(name: &N);
128 if (R >= 0) {
129 strncpy(dest: Name, src: N.nodename, n: Len);
130 return 0;
131 }
132 return R;
133}
134#endif
135
136COMPILER_RT_VISIBILITY int lprofLockFd(int fd) {
137#ifdef COMPILER_RT_HAS_FCNTL_LCK
138 struct flock s_flock;
139
140 s_flock.l_whence = SEEK_SET;
141 s_flock.l_start = 0;
142 s_flock.l_len = 0; /* Until EOF. */
143 s_flock.l_pid = getpid();
144 s_flock.l_type = F_WRLCK;
145
146 while (fcntl(fd: fd, F_SETLKW, &s_flock) == -1) {
147 if (errno != EINTR) {
148 if (errno == ENOLCK) {
149 return -1;
150 }
151 break;
152 }
153 }
154 return 0;
155#else
156 flock(fd, LOCK_EX);
157 return 0;
158#endif
159}
160
161COMPILER_RT_VISIBILITY int lprofUnlockFd(int fd) {
162#ifdef COMPILER_RT_HAS_FCNTL_LCK
163 struct flock s_flock;
164
165 s_flock.l_whence = SEEK_SET;
166 s_flock.l_start = 0;
167 s_flock.l_len = 0; /* Until EOF. */
168 s_flock.l_pid = getpid();
169 s_flock.l_type = F_UNLCK;
170
171 while (fcntl(fd: fd, F_SETLKW, &s_flock) == -1) {
172 if (errno != EINTR) {
173 if (errno == ENOLCK) {
174 return -1;
175 }
176 break;
177 }
178 }
179 return 0;
180#else
181 flock(fd, LOCK_UN);
182 return 0;
183#endif
184}
185
186COMPILER_RT_VISIBILITY int lprofLockFileHandle(FILE *F) {
187 int fd;
188#if defined(_WIN32)
189 fd = _fileno(F);
190#else
191 fd = fileno(stream: F);
192#endif
193 return lprofLockFd(fd);
194}
195
196COMPILER_RT_VISIBILITY int lprofUnlockFileHandle(FILE *F) {
197 int fd;
198#if defined(_WIN32)
199 fd = _fileno(F);
200#else
201 fd = fileno(stream: F);
202#endif
203 return lprofUnlockFd(fd);
204}
205
206COMPILER_RT_VISIBILITY FILE *lprofOpenFileEx(const char *ProfileName) {
207 FILE *f;
208 int fd;
209#ifdef COMPILER_RT_HAS_FCNTL_LCK
210 fd = open(file: ProfileName, O_RDWR | O_CREAT, 0666);
211 if (fd < 0)
212 return NULL;
213
214 if (lprofLockFd(fd) != 0)
215 PROF_WARN("Data may be corrupted during profile merging : %s\n",
216 "Fail to obtain file lock due to system limit.");
217
218 f = fdopen(fd: fd, modes: "r+b");
219#elif defined(_WIN32)
220 // FIXME: Use the wide variants to handle Unicode filenames.
221 HANDLE h = CreateFileA(ProfileName, GENERIC_READ | GENERIC_WRITE,
222 FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS,
223 FILE_ATTRIBUTE_NORMAL, 0);
224 if (h == INVALID_HANDLE_VALUE)
225 return NULL;
226
227 fd = _open_osfhandle((intptr_t)h, 0);
228 if (fd == -1) {
229 CloseHandle(h);
230 return NULL;
231 }
232
233 if (lprofLockFd(fd) != 0)
234 PROF_WARN("Data may be corrupted during profile merging : %s\n",
235 "Fail to obtain file lock due to system limit.");
236
237 f = _fdopen(fd, "r+b");
238 if (f == 0) {
239 CloseHandle(h);
240 return NULL;
241 }
242#else
243 /* Worst case no locking applied. */
244 PROF_WARN("Concurrent file access is not supported : %s\n",
245 "lack file locking");
246 fd = open(ProfileName, O_RDWR | O_CREAT, 0666);
247 if (fd < 0)
248 return NULL;
249 f = fdopen(fd, "r+b");
250#endif
251
252 return f;
253}
254
255COMPILER_RT_VISIBILITY const char *lprofGetPathPrefix(int *PrefixStrip,
256 size_t *PrefixLen) {
257 const char *Prefix = getenv(name: "GCOV_PREFIX");
258 const char *PrefixStripStr = getenv(name: "GCOV_PREFIX_STRIP");
259
260 *PrefixLen = 0;
261 *PrefixStrip = 0;
262 if (Prefix == NULL || Prefix[0] == '\0')
263 return NULL;
264
265 if (PrefixStripStr) {
266 *PrefixStrip = atoi(nptr: PrefixStripStr);
267
268 /* Negative GCOV_PREFIX_STRIP values are ignored */
269 if (*PrefixStrip < 0)
270 *PrefixStrip = 0;
271 } else {
272 *PrefixStrip = 0;
273 }
274 *PrefixLen = strlen(s: Prefix);
275
276 return Prefix;
277}
278
279COMPILER_RT_VISIBILITY void
280lprofApplyPathPrefix(char *Dest, const char *PathStr, const char *Prefix,
281 size_t PrefixLen, int PrefixStrip) {
282
283 const char *Ptr;
284 int Level;
285 const char *StrippedPathStr = PathStr;
286
287 for (Level = 0, Ptr = PathStr + 1; Level < PrefixStrip; ++Ptr) {
288 if (*Ptr == '\0')
289 break;
290
291 if (!IS_DIR_SEPARATOR(*Ptr))
292 continue;
293
294 StrippedPathStr = Ptr;
295 ++Level;
296 }
297
298 memcpy(dest: Dest, src: Prefix, n: PrefixLen);
299
300 if (!IS_DIR_SEPARATOR(Prefix[PrefixLen - 1]))
301 Dest[PrefixLen++] = DIR_SEPARATOR;
302
303 memcpy(dest: Dest + PrefixLen, src: StrippedPathStr, n: strlen(s: StrippedPathStr) + 1);
304}
305
306COMPILER_RT_VISIBILITY const char *
307lprofFindFirstDirSeparator(const char *Path) {
308 const char *Sep = strchr(s: Path, DIR_SEPARATOR);
309#if defined(DIR_SEPARATOR_2)
310 const char *Sep2 = strchr(Path, DIR_SEPARATOR_2);
311 if (Sep2 && (!Sep || Sep2 < Sep))
312 Sep = Sep2;
313#endif
314 return Sep;
315}
316
317COMPILER_RT_VISIBILITY const char *lprofFindLastDirSeparator(const char *Path) {
318 const char *Sep = strrchr(s: Path, DIR_SEPARATOR);
319#if defined(DIR_SEPARATOR_2)
320 const char *Sep2 = strrchr(Path, DIR_SEPARATOR_2);
321 if (Sep2 && (!Sep || Sep2 > Sep))
322 Sep = Sep2;
323#endif
324 return Sep;
325}
326
327COMPILER_RT_VISIBILITY int lprofSuspendSigKill(void) {
328#if defined(__linux__)
329 int PDeachSig = 0;
330 /* Temporarily suspend getting SIGKILL upon exit of the parent process. */
331 if (prctl(PR_GET_PDEATHSIG, &PDeachSig) == 0 && PDeachSig == SIGKILL)
332 prctl(PR_SET_PDEATHSIG, 0);
333 return (PDeachSig == SIGKILL);
334#elif defined(__FreeBSD__)
335 int PDeachSig = 0, PDisableSig = 0;
336 if (procctl(P_PID, 0, PROC_PDEATHSIG_STATUS, &PDeachSig) == 0 &&
337 PDeachSig == SIGKILL)
338 procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &PDisableSig);
339 return (PDeachSig == SIGKILL);
340#else
341 return 0;
342#endif
343}
344
345COMPILER_RT_VISIBILITY void lprofRestoreSigKill(void) {
346#if defined(__linux__)
347 prctl(PR_SET_PDEATHSIG, SIGKILL);
348#elif defined(__FreeBSD__)
349 int PEnableSig = SIGKILL;
350 procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &PEnableSig);
351#endif
352}
353
354COMPILER_RT_VISIBILITY int lprofReleaseMemoryPagesToOS(uintptr_t Begin,
355 uintptr_t End) {
356#if defined(__ve__)
357 // VE doesn't support madvise.
358 return 0;
359#else
360 size_t PageSize = getpagesize();
361 uintptr_t BeginAligned = lprofRoundUpTo(x: (uintptr_t)Begin, boundary: PageSize);
362 uintptr_t EndAligned = lprofRoundDownTo(x: (uintptr_t)End, boundary: PageSize);
363 if (BeginAligned < EndAligned) {
364#if defined(__Fuchsia__)
365 return _zx_vmar_op_range(_zx_vmar_root_self(), ZX_VMAR_OP_DECOMMIT,
366 (zx_vaddr_t)BeginAligned,
367 EndAligned - BeginAligned, NULL, 0);
368#else
369 return madvise(addr: (void *)BeginAligned, len: EndAligned - BeginAligned,
370 MADV_DONTNEED);
371#endif
372 }
373 return 0;
374#endif
375}
376