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 | |
49 | COMPILER_RT_VISIBILITY unsigned lprofDirMode = 0755; |
50 | |
51 | COMPILER_RT_VISIBILITY |
52 | void __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 | |
87 | COMPILER_RT_VISIBILITY |
88 | void __llvm_profile_set_dir_mode(unsigned Mode) { lprofDirMode = Mode; } |
89 | |
90 | COMPILER_RT_VISIBILITY |
91 | unsigned __llvm_profile_get_dir_mode(void) { return lprofDirMode; } |
92 | |
93 | #if COMPILER_RT_HAS_ATOMICS != 1 |
94 | COMPILER_RT_VISIBILITY |
95 | uint32_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 | } |
103 | COMPILER_RT_VISIBILITY |
104 | void *lprofPtrFetchAdd(void **Mem, long ByteIncr) { |
105 | void *Old = *Mem; |
106 | *((char **)Mem) += ByteIncr; |
107 | return Old; |
108 | } |
109 | |
110 | #endif |
111 | |
112 | #ifdef _WIN32 |
113 | COMPILER_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) |
125 | COMPILER_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 | |
136 | COMPILER_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 | |
161 | COMPILER_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 | |
186 | COMPILER_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 | |
196 | COMPILER_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 | |
206 | COMPILER_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 | |
255 | COMPILER_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 | |
279 | COMPILER_RT_VISIBILITY void |
280 | lprofApplyPathPrefix(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 | |
306 | COMPILER_RT_VISIBILITY const char * |
307 | lprofFindFirstDirSeparator(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 | |
317 | COMPILER_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 | |
327 | COMPILER_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 | |
345 | COMPILER_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 | |
354 | COMPILER_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 | |