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 _AIX |
25 | #include <sys/statfs.h> |
26 | // <sys/vmount.h> depends on `uint` to be a typedef from <sys/types.h> to |
27 | // `uint_t`; however, <sys/types.h> does not always declare `uint`. We provide |
28 | // the typedef prior to including <sys/vmount.h> to work around this issue. |
29 | typedef uint_t uint; |
30 | #include <sys/vmount.h> |
31 | #endif |
32 | |
33 | #ifdef COMPILER_RT_HAS_UNAME |
34 | #include <sys/utsname.h> |
35 | #endif |
36 | |
37 | #include <stdlib.h> |
38 | #include <string.h> |
39 | |
40 | #if defined(__linux__) |
41 | #include <signal.h> |
42 | #include <sys/prctl.h> |
43 | #endif |
44 | |
45 | #if defined(__Fuchsia__) |
46 | #include <zircon/process.h> |
47 | #include <zircon/syscalls.h> |
48 | #endif |
49 | |
50 | #if defined(__FreeBSD__) |
51 | #include <signal.h> |
52 | #include <sys/procctl.h> |
53 | #endif |
54 | |
55 | #include "InstrProfiling.h" |
56 | #include "InstrProfilingUtil.h" |
57 | |
58 | COMPILER_RT_VISIBILITY unsigned lprofDirMode = 0755; |
59 | |
60 | COMPILER_RT_VISIBILITY |
61 | void __llvm_profile_recursive_mkdir(char *path) { |
62 | int i; |
63 | int start = 1; |
64 | |
65 | #if defined(__ANDROID__) && defined(__ANDROID_API__) && \ |
66 | defined(__ANDROID_API_FUTURE__) && \ |
67 | __ANDROID_API__ == __ANDROID_API_FUTURE__ |
68 | // Avoid spammy selinux denial messages in Android by not attempting to |
69 | // create directories in GCOV_PREFIX. These denials occur when creating (or |
70 | // even attempting to stat()) top-level directories like "/data". |
71 | // |
72 | // Do so by ignoring ${GCOV_PREFIX} when invoking mkdir(). |
73 | const char *gcov_prefix = getenv("GCOV_PREFIX" ); |
74 | if (gcov_prefix != NULL) { |
75 | const int gcov_prefix_len = strlen(gcov_prefix); |
76 | if (strncmp(path, gcov_prefix, gcov_prefix_len) == 0) |
77 | start = gcov_prefix_len; |
78 | } |
79 | #endif |
80 | |
81 | for (i = start; path[i] != '\0'; ++i) { |
82 | char save = path[i]; |
83 | if (!IS_DIR_SEPARATOR(path[i])) |
84 | continue; |
85 | path[i] = '\0'; |
86 | #ifdef _WIN32 |
87 | _mkdir(path); |
88 | #else |
89 | /* Some of these will fail, ignore it. */ |
90 | mkdir(path: path, mode: __llvm_profile_get_dir_mode()); |
91 | #endif |
92 | path[i] = save; |
93 | } |
94 | } |
95 | |
96 | COMPILER_RT_VISIBILITY |
97 | void __llvm_profile_set_dir_mode(unsigned Mode) { lprofDirMode = Mode; } |
98 | |
99 | COMPILER_RT_VISIBILITY |
100 | unsigned __llvm_profile_get_dir_mode(void) { return lprofDirMode; } |
101 | |
102 | #if COMPILER_RT_HAS_ATOMICS != 1 |
103 | COMPILER_RT_VISIBILITY |
104 | uint32_t lprofBoolCmpXchg(void **Ptr, void *OldV, void *NewV) { |
105 | void *R = *Ptr; |
106 | if (R == OldV) { |
107 | *Ptr = NewV; |
108 | return 1; |
109 | } |
110 | return 0; |
111 | } |
112 | COMPILER_RT_VISIBILITY |
113 | void *lprofPtrFetchAdd(void **Mem, long ByteIncr) { |
114 | void *Old = *Mem; |
115 | *((char **)Mem) += ByteIncr; |
116 | return Old; |
117 | } |
118 | |
119 | #endif |
120 | |
121 | #ifdef _WIN32 |
122 | COMPILER_RT_VISIBILITY int lprofGetHostName(char *Name, int Len) { |
123 | WCHAR Buffer[COMPILER_RT_MAX_HOSTLEN]; |
124 | DWORD BufferSize = sizeof(Buffer); |
125 | BOOL Result = |
126 | GetComputerNameExW(ComputerNameDnsFullyQualified, Buffer, &BufferSize); |
127 | if (!Result) |
128 | return -1; |
129 | if (WideCharToMultiByte(CP_UTF8, 0, Buffer, -1, Name, Len, NULL, NULL) == 0) |
130 | return -1; |
131 | return 0; |
132 | } |
133 | #elif defined(COMPILER_RT_HAS_UNAME) |
134 | COMPILER_RT_VISIBILITY int lprofGetHostName(char *Name, int Len) { |
135 | struct utsname N; |
136 | int R = uname(name: &N); |
137 | if (R >= 0) { |
138 | strncpy(dest: Name, src: N.nodename, n: Len); |
139 | return 0; |
140 | } |
141 | return R; |
142 | } |
143 | #endif |
144 | |
145 | COMPILER_RT_VISIBILITY int lprofLockFd(int fd) { |
146 | #ifdef COMPILER_RT_HAS_FCNTL_LCK |
147 | struct flock s_flock; |
148 | |
149 | s_flock.l_whence = SEEK_SET; |
150 | s_flock.l_start = 0; |
151 | s_flock.l_len = 0; /* Until EOF. */ |
152 | s_flock.l_pid = getpid(); |
153 | s_flock.l_type = F_WRLCK; |
154 | |
155 | while (fcntl(fd: fd, F_SETLKW, &s_flock) == -1) { |
156 | if (errno != EINTR) { |
157 | if (errno == ENOLCK) { |
158 | return -1; |
159 | } |
160 | break; |
161 | } |
162 | } |
163 | return 0; |
164 | #elif defined(COMPILER_RT_HAS_FLOCK) || defined(_WIN32) |
165 | // Windows doesn't have flock but WindowsMMap.h provides a shim |
166 | flock(fd, LOCK_EX); |
167 | return 0; |
168 | #else |
169 | return 0; |
170 | #endif |
171 | } |
172 | |
173 | COMPILER_RT_VISIBILITY int lprofUnlockFd(int fd) { |
174 | #ifdef COMPILER_RT_HAS_FCNTL_LCK |
175 | struct flock s_flock; |
176 | |
177 | s_flock.l_whence = SEEK_SET; |
178 | s_flock.l_start = 0; |
179 | s_flock.l_len = 0; /* Until EOF. */ |
180 | s_flock.l_pid = getpid(); |
181 | s_flock.l_type = F_UNLCK; |
182 | |
183 | while (fcntl(fd: fd, F_SETLKW, &s_flock) == -1) { |
184 | if (errno != EINTR) { |
185 | if (errno == ENOLCK) { |
186 | return -1; |
187 | } |
188 | break; |
189 | } |
190 | } |
191 | return 0; |
192 | #elif defined(COMPILER_RT_HAS_FLOCK) || defined(_WIN32) |
193 | // Windows doesn't have flock but WindowsMMap.h provides a shim |
194 | flock(fd, LOCK_UN); |
195 | return 0; |
196 | #else |
197 | return 0; |
198 | #endif |
199 | } |
200 | |
201 | COMPILER_RT_VISIBILITY int lprofLockFileHandle(FILE *F) { |
202 | int fd; |
203 | #if defined(_WIN32) |
204 | fd = _fileno(F); |
205 | #else |
206 | fd = fileno(stream: F); |
207 | #endif |
208 | return lprofLockFd(fd); |
209 | } |
210 | |
211 | COMPILER_RT_VISIBILITY int lprofUnlockFileHandle(FILE *F) { |
212 | int fd; |
213 | #if defined(_WIN32) |
214 | fd = _fileno(F); |
215 | #else |
216 | fd = fileno(stream: F); |
217 | #endif |
218 | return lprofUnlockFd(fd); |
219 | } |
220 | |
221 | COMPILER_RT_VISIBILITY FILE *lprofOpenFileEx(const char *ProfileName) { |
222 | FILE *f; |
223 | int fd; |
224 | #ifdef COMPILER_RT_HAS_FCNTL_LCK |
225 | fd = open(file: ProfileName, O_RDWR | O_CREAT, 0666); |
226 | if (fd < 0) |
227 | return NULL; |
228 | |
229 | if (lprofLockFd(fd) != 0) |
230 | PROF_WARN("Data may be corrupted during profile merging : %s\n" , |
231 | "Fail to obtain file lock due to system limit." ); |
232 | |
233 | f = fdopen(fd: fd, modes: "r+b" ); |
234 | #elif defined(_WIN32) |
235 | // FIXME: Use the wide variants to handle Unicode filenames. |
236 | HANDLE h = CreateFileA(ProfileName, GENERIC_READ | GENERIC_WRITE, |
237 | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, |
238 | FILE_ATTRIBUTE_NORMAL, 0); |
239 | if (h == INVALID_HANDLE_VALUE) |
240 | return NULL; |
241 | |
242 | fd = _open_osfhandle((intptr_t)h, 0); |
243 | if (fd == -1) { |
244 | CloseHandle(h); |
245 | return NULL; |
246 | } |
247 | |
248 | if (lprofLockFd(fd) != 0) |
249 | PROF_WARN("Data may be corrupted during profile merging : %s\n" , |
250 | "Fail to obtain file lock due to system limit." ); |
251 | |
252 | f = _fdopen(fd, "r+b" ); |
253 | if (f == 0) { |
254 | CloseHandle(h); |
255 | return NULL; |
256 | } |
257 | #else |
258 | /* Worst case no locking applied. */ |
259 | PROF_WARN("Concurrent file access is not supported : %s\n" , |
260 | "lack file locking" ); |
261 | fd = open(ProfileName, O_RDWR | O_CREAT, 0666); |
262 | if (fd < 0) |
263 | return NULL; |
264 | f = fdopen(fd, "r+b" ); |
265 | #endif |
266 | |
267 | return f; |
268 | } |
269 | |
270 | #if defined(_AIX) |
271 | // Return 1 (true) if the file descriptor Fd represents a file that is on a |
272 | // local filesystem, otherwise return 0. |
273 | static int isLocalFilesystem(int Fd) { |
274 | struct statfs Vfs; |
275 | if (fstatfs(Fd, &Vfs) != 0) { |
276 | PROF_ERR("%s: fstatfs(%d) failed: %s\n" , __func__, Fd, strerror(errno)); |
277 | return 0; |
278 | } |
279 | |
280 | int Ret; |
281 | size_t BufSize = 2048u; |
282 | char *Buf; |
283 | int Tries = 3; |
284 | while (Tries--) { |
285 | Buf = malloc(BufSize); |
286 | // mntctl returns -1 if `Buf` is `NULL`. |
287 | Ret = mntctl(MCTL_QUERY, BufSize, Buf); |
288 | if (Ret != 0) |
289 | break; |
290 | BufSize = *(unsigned int *)Buf; |
291 | free(Buf); |
292 | } |
293 | |
294 | if (Ret != -1) { |
295 | // Look for the correct vmount entry. |
296 | char *CurObjPtr = Buf; |
297 | while (Ret--) { |
298 | struct vmount *Vp = (struct vmount *)CurObjPtr; |
299 | _Static_assert(sizeof(Vfs.f_fsid) == sizeof(Vp->vmt_fsid), |
300 | "fsid length mismatch" ); |
301 | if (memcmp(&Vfs.f_fsid, &Vp->vmt_fsid, sizeof Vfs.f_fsid) == 0) { |
302 | int Answer = (Vp->vmt_flags & MNT_REMOTE) == 0; |
303 | free(Buf); |
304 | return Answer; |
305 | } |
306 | CurObjPtr += Vp->vmt_length; |
307 | } |
308 | } |
309 | |
310 | free(Buf); |
311 | // There was an error in mntctl or vmount entry not found; "remote" is the |
312 | // conservative answer. |
313 | return 0; |
314 | } |
315 | #endif |
316 | |
317 | static int isMmapSafe(int Fd) { |
318 | if (getenv(name: "LLVM_PROFILE_NO_MMAP" )) // For testing purposes. |
319 | return 0; |
320 | #ifdef _AIX |
321 | return isLocalFilesystem(Fd); |
322 | #else |
323 | return 1; |
324 | #endif |
325 | } |
326 | |
327 | COMPILER_RT_VISIBILITY void lprofGetFileContentBuffer(FILE *F, uint64_t Length, |
328 | ManagedMemory *Buf) { |
329 | Buf->Status = MS_INVALID; |
330 | if (isMmapSafe(Fd: fileno(stream: F))) { |
331 | Buf->Addr = |
332 | mmap(NULL, len: Length, PROT_READ, MAP_SHARED | MAP_FILE, fd: fileno(stream: F), offset: 0); |
333 | if (Buf->Addr == MAP_FAILED) |
334 | PROF_ERR("%s: mmap failed: %s\n" , __func__, strerror(errno)) |
335 | else |
336 | Buf->Status = MS_MMAP; |
337 | return; |
338 | } |
339 | |
340 | if (getenv(name: "LLVM_PROFILE_VERBOSE" )) |
341 | PROF_NOTE("%s\n" , "could not use mmap; using fread instead" ); |
342 | |
343 | void *Buffer = malloc(size: Length); |
344 | if (!Buffer) { |
345 | PROF_ERR("%s: malloc failed: %s\n" , __func__, strerror(errno)); |
346 | return; |
347 | } |
348 | if (ftell(stream: F) != 0) { |
349 | PROF_ERR("%s: expecting ftell to return zero\n" , __func__); |
350 | free(ptr: Buffer); |
351 | return; |
352 | } |
353 | |
354 | // Read the entire file into memory. |
355 | size_t BytesRead = fread(ptr: Buffer, size: 1, n: Length, stream: F); |
356 | if (BytesRead != (size_t)Length) { |
357 | PROF_ERR("%s: fread failed%s\n" , __func__, |
358 | feof(F) ? ": end of file reached" : "" ); |
359 | free(ptr: Buffer); |
360 | return; |
361 | } |
362 | |
363 | // Reading was successful, record the result in the Buf parameter. |
364 | Buf->Addr = Buffer; |
365 | Buf->Status = MS_MALLOC; |
366 | } |
367 | |
368 | COMPILER_RT_VISIBILITY |
369 | void lprofReleaseBuffer(ManagedMemory *Buf, size_t Length) { |
370 | switch (Buf->Status) { |
371 | case MS_MALLOC: |
372 | free(ptr: Buf->Addr); |
373 | break; |
374 | case MS_MMAP: |
375 | (void)munmap(addr: Buf->Addr, len: Length); |
376 | break; |
377 | default: |
378 | PROF_ERR("%s: Buffer has invalid state: %d\n" , __func__, Buf->Status); |
379 | break; |
380 | } |
381 | Buf->Addr = NULL; |
382 | Buf->Status = MS_INVALID; |
383 | } |
384 | |
385 | COMPILER_RT_VISIBILITY const char *lprofGetPathPrefix(int *PrefixStrip, |
386 | size_t *PrefixLen) { |
387 | const char *Prefix = getenv(name: "GCOV_PREFIX" ); |
388 | const char *PrefixStripStr = getenv(name: "GCOV_PREFIX_STRIP" ); |
389 | |
390 | *PrefixLen = 0; |
391 | *PrefixStrip = 0; |
392 | if (Prefix == NULL || Prefix[0] == '\0') |
393 | return NULL; |
394 | |
395 | if (PrefixStripStr) { |
396 | *PrefixStrip = atoi(nptr: PrefixStripStr); |
397 | |
398 | /* Negative GCOV_PREFIX_STRIP values are ignored */ |
399 | if (*PrefixStrip < 0) |
400 | *PrefixStrip = 0; |
401 | } else { |
402 | *PrefixStrip = 0; |
403 | } |
404 | *PrefixLen = strlen(s: Prefix); |
405 | |
406 | return Prefix; |
407 | } |
408 | |
409 | COMPILER_RT_VISIBILITY void |
410 | lprofApplyPathPrefix(char *Dest, const char *PathStr, const char *Prefix, |
411 | size_t PrefixLen, int PrefixStrip) { |
412 | |
413 | const char *Ptr; |
414 | int Level; |
415 | const char *StrippedPathStr = PathStr; |
416 | |
417 | for (Level = 0, Ptr = PathStr + 1; Level < PrefixStrip; ++Ptr) { |
418 | if (*Ptr == '\0') |
419 | break; |
420 | |
421 | if (!IS_DIR_SEPARATOR(*Ptr)) |
422 | continue; |
423 | |
424 | StrippedPathStr = Ptr; |
425 | ++Level; |
426 | } |
427 | |
428 | memcpy(dest: Dest, src: Prefix, n: PrefixLen); |
429 | |
430 | if (!IS_DIR_SEPARATOR(Prefix[PrefixLen - 1])) |
431 | Dest[PrefixLen++] = DIR_SEPARATOR; |
432 | |
433 | memcpy(dest: Dest + PrefixLen, src: StrippedPathStr, n: strlen(s: StrippedPathStr) + 1); |
434 | } |
435 | |
436 | COMPILER_RT_VISIBILITY const char * |
437 | lprofFindFirstDirSeparator(const char *Path) { |
438 | const char *Sep = strchr(s: Path, DIR_SEPARATOR); |
439 | #if defined(DIR_SEPARATOR_2) |
440 | const char *Sep2 = strchr(Path, DIR_SEPARATOR_2); |
441 | if (Sep2 && (!Sep || Sep2 < Sep)) |
442 | Sep = Sep2; |
443 | #endif |
444 | return Sep; |
445 | } |
446 | |
447 | COMPILER_RT_VISIBILITY const char *lprofFindLastDirSeparator(const char *Path) { |
448 | const char *Sep = strrchr(s: Path, DIR_SEPARATOR); |
449 | #if defined(DIR_SEPARATOR_2) |
450 | const char *Sep2 = strrchr(Path, DIR_SEPARATOR_2); |
451 | if (Sep2 && (!Sep || Sep2 > Sep)) |
452 | Sep = Sep2; |
453 | #endif |
454 | return Sep; |
455 | } |
456 | |
457 | COMPILER_RT_VISIBILITY int lprofSuspendSigKill(void) { |
458 | #if defined(__linux__) |
459 | int PDeachSig = 0; |
460 | /* Temporarily suspend getting SIGKILL upon exit of the parent process. */ |
461 | if (prctl(PR_GET_PDEATHSIG, &PDeachSig) == 0 && PDeachSig == SIGKILL) |
462 | prctl(PR_SET_PDEATHSIG, 0); |
463 | return (PDeachSig == SIGKILL); |
464 | #elif defined(__FreeBSD__) |
465 | int PDeachSig = 0, PDisableSig = 0; |
466 | if (procctl(P_PID, 0, PROC_PDEATHSIG_STATUS, &PDeachSig) == 0 && |
467 | PDeachSig == SIGKILL) |
468 | procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &PDisableSig); |
469 | return (PDeachSig == SIGKILL); |
470 | #else |
471 | return 0; |
472 | #endif |
473 | } |
474 | |
475 | COMPILER_RT_VISIBILITY void lprofRestoreSigKill(void) { |
476 | #if defined(__linux__) |
477 | prctl(PR_SET_PDEATHSIG, SIGKILL); |
478 | #elif defined(__FreeBSD__) |
479 | int PEnableSig = SIGKILL; |
480 | procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &PEnableSig); |
481 | #endif |
482 | } |
483 | |
484 | COMPILER_RT_VISIBILITY int lprofReleaseMemoryPagesToOS(uintptr_t Begin, |
485 | uintptr_t End) { |
486 | #if defined(__ve__) || defined(__wasi__) |
487 | // VE and WASI doesn't support madvise. |
488 | return 0; |
489 | #else |
490 | size_t PageSize = getpagesize(); |
491 | uintptr_t BeginAligned = lprofRoundUpTo(x: (uintptr_t)Begin, boundary: PageSize); |
492 | uintptr_t EndAligned = lprofRoundDownTo(x: (uintptr_t)End, boundary: PageSize); |
493 | if (BeginAligned < EndAligned) { |
494 | #if defined(__Fuchsia__) |
495 | return _zx_vmar_op_range(_zx_vmar_root_self(), ZX_VMAR_OP_DECOMMIT, |
496 | (zx_vaddr_t)BeginAligned, |
497 | EndAligned - BeginAligned, NULL, 0); |
498 | #else |
499 | return madvise(addr: (void *)BeginAligned, len: EndAligned - BeginAligned, |
500 | MADV_DONTNEED); |
501 | #endif |
502 | } |
503 | return 0; |
504 | #endif |
505 | } |
506 | |
507 | #ifdef _AIX |
508 | typedef struct fn_node { |
509 | AtExit_Fn_ptr func; |
510 | struct fn_node *next; |
511 | } fn_node; |
512 | typedef struct { |
513 | fn_node *top; |
514 | } fn_stack; |
515 | |
516 | static void fn_stack_push(fn_stack *, AtExit_Fn_ptr); |
517 | static AtExit_Fn_ptr fn_stack_pop(fn_stack *); |
518 | /* return 1 if stack is empty, 0 otherwise */ |
519 | static int fn_stack_is_empty(fn_stack *); |
520 | |
521 | static fn_stack AtExit_stack = {0}; |
522 | #define ATEXIT_STACK (&AtExit_stack) |
523 | |
524 | /* On AIX, atexit() functions registered by a shared library do not get called |
525 | * when the library is dlclose'd, causing a crash when they are eventually |
526 | * called at main program exit. However, a destructor does get called. So we |
527 | * collect all atexit functions registered by profile-rt and at program |
528 | * termination time (normal exit, shared library unload, or dlclose) we walk |
529 | * the list and execute any function that is still sitting in the atexit system |
530 | * queue. |
531 | */ |
532 | __attribute__((__destructor__)) static void cleanup() { |
533 | while (!fn_stack_is_empty(ATEXIT_STACK)) { |
534 | AtExit_Fn_ptr func = fn_stack_pop(ATEXIT_STACK); |
535 | if (func && unatexit(func) == 0) |
536 | func(); |
537 | } |
538 | } |
539 | |
540 | static void fn_stack_push(fn_stack *st, AtExit_Fn_ptr func) { |
541 | fn_node *old_top, *n = (fn_node *)malloc(sizeof(fn_node)); |
542 | n->func = func; |
543 | |
544 | while (1) { |
545 | old_top = st->top; |
546 | n->next = old_top; |
547 | if (COMPILER_RT_BOOL_CMPXCHG(&st->top, old_top, n)) |
548 | return; |
549 | } |
550 | } |
551 | static AtExit_Fn_ptr fn_stack_pop(fn_stack *st) { |
552 | fn_node *old_top, *new_top; |
553 | while (1) { |
554 | old_top = st->top; |
555 | if (old_top == 0) |
556 | return 0; |
557 | new_top = old_top->next; |
558 | if (COMPILER_RT_BOOL_CMPXCHG(&st->top, old_top, new_top)) { |
559 | AtExit_Fn_ptr func = old_top->func; |
560 | free(old_top); |
561 | return func; |
562 | } |
563 | } |
564 | } |
565 | |
566 | static int fn_stack_is_empty(fn_stack *st) { return st->top == 0; } |
567 | #endif |
568 | |
569 | COMPILER_RT_VISIBILITY int lprofAtExit(AtExit_Fn_ptr func) { |
570 | #ifdef _AIX |
571 | fn_stack_push(ATEXIT_STACK, func); |
572 | #endif |
573 | return atexit(func: func); |
574 | } |
575 | |