Skip to content

Commit b817019

Browse files
author
Wael Yehia
committed
[profile] Implement a non-mmap path when reading profile files from a non-local filesystem
1 parent 9ef7287 commit b817019

File tree

5 files changed

+198
-29
lines changed

5 files changed

+198
-29
lines changed

compiler-rt/lib/profile/InstrProfilingFile.c

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -428,17 +428,18 @@ static int getProfileFileSizeForMerging(FILE *ProfileFile,
428428
* \p ProfileBuffer. Returns -1 on failure. On success, the caller is
429429
* responsible for unmapping the mmap'd buffer in \p ProfileBuffer. */
430430
static int mmapProfileForMerging(FILE *ProfileFile, uint64_t ProfileFileSize,
431-
char **ProfileBuffer) {
432-
*ProfileBuffer = mmap(NULL, ProfileFileSize, PROT_READ, MAP_SHARED | MAP_FILE,
433-
fileno(ProfileFile), 0);
434-
if (*ProfileBuffer == MAP_FAILED) {
431+
ManagedMemory *ProfileBuffer) {
432+
lprofGetFileContentBuffer(ProfileFile, ProfileFileSize, ProfileBuffer);
433+
434+
if (ProfileBuffer->Status == MM_INVALID) {
435435
PROF_ERR("Unable to merge profile data, mmap failed: %s\n",
436436
strerror(errno));
437437
return -1;
438438
}
439439

440-
if (__llvm_profile_check_compatibility(*ProfileBuffer, ProfileFileSize)) {
441-
(void)munmap(*ProfileBuffer, ProfileFileSize);
440+
if (__llvm_profile_check_compatibility(ProfileBuffer->Addr,
441+
ProfileFileSize)) {
442+
(void)lprofReleaseBuffer(ProfileBuffer, ProfileFileSize);
442443
PROF_WARN("Unable to merge profile data: %s\n",
443444
"source profile file is not compatible.");
444445
return -1;
@@ -453,7 +454,7 @@ static int mmapProfileForMerging(FILE *ProfileFile, uint64_t ProfileFileSize,
453454
*/
454455
static int doProfileMerging(FILE *ProfileFile, int *MergeDone) {
455456
uint64_t ProfileFileSize;
456-
char *ProfileBuffer;
457+
ManagedMemory ProfileBuffer;
457458

458459
/* Get the size of the profile on disk. */
459460
if (getProfileFileSizeForMerging(ProfileFile, &ProfileFileSize) == -1)
@@ -469,9 +470,9 @@ static int doProfileMerging(FILE *ProfileFile, int *MergeDone) {
469470
return -1;
470471

471472
/* Now start merging */
472-
if (__llvm_profile_merge_from_buffer(ProfileBuffer, ProfileFileSize)) {
473+
if (__llvm_profile_merge_from_buffer(ProfileBuffer.Addr, ProfileFileSize)) {
473474
PROF_ERR("%s\n", "Invalid profile data to merge");
474-
(void)munmap(ProfileBuffer, ProfileFileSize);
475+
(void)lprofReleaseBuffer(&ProfileBuffer, ProfileFileSize);
475476
return -1;
476477
}
477478

@@ -480,7 +481,7 @@ static int doProfileMerging(FILE *ProfileFile, int *MergeDone) {
480481
(void)COMPILER_RT_FTRUNCATE(ProfileFile,
481482
__llvm_profile_get_size_for_buffer());
482483

483-
(void)munmap(ProfileBuffer, ProfileFileSize);
484+
(void)lprofReleaseBuffer(&ProfileBuffer, ProfileFileSize);
484485
*MergeDone = 1;
485486

486487
return 0;
@@ -702,13 +703,13 @@ static void initializeProfileForContinuousMode(void) {
702703
} else {
703704
/* The merged profile has a non-zero length. Check that it is compatible
704705
* with the data in this process. */
705-
char *ProfileBuffer;
706+
ManagedMemory ProfileBuffer;
706707
if (mmapProfileForMerging(File, ProfileFileSize, &ProfileBuffer) == -1) {
707708
lprofUnlockFileHandle(File);
708709
fclose(File);
709710
return;
710711
}
711-
(void)munmap(ProfileBuffer, ProfileFileSize);
712+
(void)lprofReleaseBuffer(&ProfileBuffer, ProfileFileSize);
712713
}
713714
} else {
714715
File = fopen(Filename, FileOpenMode);
@@ -1346,12 +1347,12 @@ COMPILER_RT_VISIBILITY int __llvm_profile_set_file_object(FILE *File,
13461347
} else {
13471348
/* The merged profile has a non-zero length. Check that it is compatible
13481349
* with the data in this process. */
1349-
char *ProfileBuffer;
1350+
ManagedMemory ProfileBuffer;
13501351
if (mmapProfileForMerging(File, ProfileFileSize, &ProfileBuffer) == -1) {
13511352
lprofUnlockFileHandle(File);
13521353
return 1;
13531354
}
1354-
(void)munmap(ProfileBuffer, ProfileFileSize);
1355+
(void)lprofReleaseBuffer(&ProfileBuffer, ProfileFileSize);
13551356
}
13561357
mmapForContinuousMode(0, File);
13571358
lprofUnlockFileHandle(File);

compiler-rt/lib/profile/InstrProfilingUtil.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@
2121
#include <unistd.h>
2222
#endif
2323

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+
2433
#ifdef COMPILER_RT_HAS_UNAME
2534
#include <sys/utsname.h>
2635
#endif
@@ -258,6 +267,113 @@ COMPILER_RT_VISIBILITY FILE *lprofOpenFileEx(const char *ProfileName) {
258267
return f;
259268
}
260269

270+
// Return 1 (true) if the file descriptor Fd represents a file that is on a
271+
// local filesystem, otherwise return 0.
272+
static int is_local_filesystem(int Fd) {
273+
#if defined(_AIX)
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+
Ret = mntctl(MCTL_QUERY, BufSize, Buf);
287+
if (Ret != 0)
288+
break;
289+
BufSize = *(unsigned int *)Buf;
290+
free(Buf);
291+
}
292+
293+
if (Ret != -1) {
294+
// Look for the correct vmount entry.
295+
char *CurObjPtr = Buf;
296+
while (Ret--) {
297+
struct vmount *Vp = (struct vmount *)CurObjPtr;
298+
_Static_assert(sizeof(Vfs.f_fsid) == sizeof(Vp->vmt_fsid),
299+
"fsid length mismatch");
300+
if (memcmp(&Vfs.f_fsid, &Vp->vmt_fsid, sizeof Vfs.f_fsid) == 0) {
301+
free(Buf);
302+
return (Vp->vmt_flags & MNT_REMOTE) == 0;
303+
}
304+
CurObjPtr += Vp->vmt_length;
305+
}
306+
}
307+
308+
free(Buf);
309+
// There was an error in mntctl or vmount entry not found; "remote" is the
310+
// conservative answer.
311+
#endif
312+
return 0;
313+
}
314+
315+
static int isMmapSafe(int Fd) {
316+
if (getenv("LLVM_NO_MMAP")) // For testing purposes.
317+
return 0;
318+
(void)&is_local_filesystem; // a fake reference to satisfy -Wunused-function
319+
#ifdef _AIX
320+
return is_local_filesystem(Fd);
321+
#endif
322+
return 1;
323+
}
324+
325+
COMPILER_RT_VISIBILITY void lprofGetFileContentBuffer(FILE *F, uint64_t Length,
326+
ManagedMemory *Buf) {
327+
Buf->Status = MM_INVALID;
328+
329+
if (!F || isMmapSafe(fileno(F))) {
330+
Buf->Addr = mmap(NULL, Length, PROT_READ, MAP_SHARED | MAP_FILE,
331+
F ? fileno(F) : -1, 0);
332+
if (Buf->Addr != MAP_FAILED)
333+
Buf->Status = MM_MMAP;
334+
return;
335+
}
336+
337+
if (getenv("LLVM_PROFILE_VERBOSE"))
338+
PROF_NOTE("Could not use mmap; using fread instead.%s\n", "");
339+
340+
void *Buffer = malloc(Length);
341+
342+
if (!Buffer) {
343+
PROF_ERR("%s: malloc failed: %s\n", __func__, strerror(errno));
344+
return;
345+
}
346+
if (fseek(F, 0L, SEEK_SET) != 0) {
347+
PROF_ERR("%s: fseek(0, SEEK_SET) failed: %s\n", __func__, strerror(errno));
348+
return;
349+
}
350+
351+
// Read the entire file into memory.
352+
size_t BytesRead = fread(Buffer, 1, Length, F);
353+
if (BytesRead != (size_t)Length || ferror(F)) {
354+
PROF_ERR("%s: fread failed: %s\n", __func__, strerror(errno));
355+
return;
356+
}
357+
358+
// Reading was successful, record the result in the Buf parameter.
359+
Buf->Addr = Buffer;
360+
Buf->Status = MM_MALLOC;
361+
}
362+
363+
void lprofReleaseBuffer(ManagedMemory *Buf, size_t Length) {
364+
switch (Buf->Status) {
365+
case MM_MALLOC:
366+
free(Buf->Addr);
367+
break;
368+
case MM_MMAP:
369+
munmap(Buf->Addr, Length);
370+
break;
371+
case MM_INVALID:
372+
PROF_ERR("%s: Buffer has invalid state: %d", __func__, Buf->Status);
373+
break;
374+
}
375+
}
376+
261377
COMPILER_RT_VISIBILITY const char *lprofGetPathPrefix(int *PrefixStrip,
262378
size_t *PrefixLen) {
263379
const char *Prefix = getenv("GCOV_PREFIX");

compiler-rt/lib/profile/InstrProfilingUtil.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,25 @@ int lprofUnlockFileHandle(FILE *F);
3131
* lock for exclusive access. The caller will block
3232
* if the lock is already held by another process. */
3333
FILE *lprofOpenFileEx(const char *Filename);
34+
35+
enum MemoryStatus {
36+
MM_INVALID = 0x1, // Addr is not a valid address
37+
MM_MMAP = 0x2, // Addr was mmap'ed
38+
MM_MALLOC = 0x4 // Addr was malloc'ed
39+
};
40+
typedef struct {
41+
void *Addr;
42+
enum MemoryStatus Status;
43+
} ManagedMemory;
44+
45+
/* Read the content of a file using mmap or fread into a buffer.
46+
* Certain files (e.g. NFS mounted) cannot be opened reliably with mmap,
47+
* so we use fread in those cases. The corresponding lprofReleaseBuffer
48+
* will free/munmap the buffer.
49+
*/
50+
void lprofGetFileContentBuffer(FILE *F, uint64_t FileSize, ManagedMemory *Buf);
51+
void lprofReleaseBuffer(ManagedMemory *FileBuffer, size_t Length);
52+
3453
/* PS4 doesn't have setenv/getenv/fork. Define a shim. */
3554
#if __ORBIS__
3655
#include <sys/types.h>

compiler-rt/test/profile/Posix/instrprof-fork.c

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33
// RUN: %clang_pgogen=%t.profdir -o %t -O2 %s
44
// RUN: %run %t
55
// RUN: llvm-profdata show --all-functions --counts %t.profdir/default_*.profraw | FileCheck %s
6+
// RUN: rm -fr %t.profdir
7+
// RUN: env LLVM_NO_MMAP=1 %run %t
8+
// RUN: llvm-profdata show --all-functions --counts %t.profdir/default_*.profraw | FileCheck %s
9+
610
//
711
// CHECK: func1:
8-
// CHECK: Block counts: [4]
12+
// CHECK: Block counts: [21]
913
// CHECK: func2:
10-
// CHECK: Block counts: [1]
14+
// CHECK: Block counts: [10]
1115

1216
#include <sys/wait.h>
1317
#include <unistd.h>
@@ -16,17 +20,21 @@ __attribute__((noinline)) void func1() {}
1620
__attribute__((noinline)) void func2() {}
1721

1822
int main(void) {
19-
// child | parent
20-
int status; // func1 func2 | func1 func2
21-
func1(); // +1 | +1 (*)
22-
pid_t pid = fork(); // |
23-
if (pid == -1) // |
24-
return 1; // |
25-
if (pid == 0) // |
26-
func2(); // +1 |
27-
func1(); // +1 | +1
28-
if (pid) // ------------+------------
29-
wait(&status); // 2 1 | 2 0
30-
return 0; // (*) the child inherits counter values prior to fork
31-
// from the parent in non-continuous mode.
23+
// child | parent
24+
// func1 func2 | func1 func2
25+
func1(); // +10 | +1 (*)
26+
int i = 10; // |
27+
while (i-- > 0) { // |
28+
pid_t pid = fork(); // |
29+
if (pid == -1) // |
30+
return 1; // |
31+
if (pid == 0) { // |
32+
func2(); // +10 |
33+
func1(); // +10 |
34+
return 0; // |
35+
} // |
36+
} // ------------+------------
37+
int status; // 20 10 | 1 0
38+
wait(&status); // (*) the child inherits counter values prior to fork
39+
return 0; // from the parent in non-continuous mode.
3240
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: mkdir -p %t.d && cd %t.d
2+
// RUN: rm -f *.profraw
3+
// RUN: %clang_pgogen %s -o a.out
4+
5+
// Need to run a.out twice, the second time a merge will occur which will trigger an mmap.
6+
// RUN: ./a.out
7+
// RUN: llvm-profdata show default_*.profraw --all-functions --counts --memop-sizes 2>&1 | FileCheck %s -check-prefix=PROFDATA
8+
// RUN: env LLVM_NO_MMAP=1 LLVM_PROFILE_VERBOSE=1 ./a.out 2>&1 | FileCheck %s
9+
// RUN: llvm-profdata show default_*.profraw --all-functions --counts --memop-sizes 2>&1 | FileCheck %s -check-prefix=PROFDATA2
10+
11+
// CHECK: Could not use mmap; using fread instead.
12+
// PROFDATA: Block counts: [1]
13+
// PROFDATA: [ 0, 0, 1 ]
14+
// PROFDATA: Maximum function count: 1
15+
// PROFDATA2: Block counts: [2]
16+
// PROFDATA2: [ 0, 0, 2 ]
17+
// PROFDATA2: Maximum function count: 2
18+
19+
#include <string.h>
20+
int ar[8];
21+
int main() {
22+
memcpy(ar, ar + 2, ar[0]);
23+
memcpy(ar, ar + 2, ar[2]);
24+
return ar[2];
25+
}

0 commit comments

Comments
 (0)