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, __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(&N);
128  if (R >= 0) {
129    strncpy(Name, N.nodename, 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, 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, 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(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(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(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, "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("GCOV_PREFIX");
258  const char *PrefixStripStr = getenv("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(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(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, Prefix, PrefixLen);
299
300  if (!IS_DIR_SEPARATOR(Prefix[PrefixLen - 1]))
301    Dest[PrefixLen++] = DIR_SEPARATOR;
302
303  memcpy(Dest + PrefixLen, StrippedPathStr, strlen(StrippedPathStr) + 1);
304}
305
306COMPILER_RT_VISIBILITY const char *
307lprofFindFirstDirSeparator(const char *Path) {
308  const char *Sep = strchr(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(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((uintptr_t)Begin, PageSize);
362  uintptr_t EndAligned = lprofRoundDownTo((uintptr_t)End, 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((void *)BeginAligned, EndAligned - BeginAligned,
370                   MADV_DONTNEED);
371#endif
372  }
373  return 0;
374#endif
375}
376