139 changes: 137 additions & 2 deletions compiler-rt/lib/profile/InstrProfilingFile.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#include "InstrProfiling.h"
#include "InstrProfilingInternal.h"
#include "InstrProfilingPort.h"
#include "InstrProfilingUtil.h"

/* From where is profile name specified.
Expand Down Expand Up @@ -100,6 +101,12 @@ static void setProfileFile(FILE *File) { ProfileFile = File; }

COMPILER_RT_VISIBILITY void __llvm_profile_set_file_object(FILE *File,
int EnableMerge) {
if (__llvm_profile_is_continuous_mode_enabled()) {
PROF_WARN("__llvm_profile_set_file_object(fd=%d) not supported, because "
"continuous sync mode (%%c) is enabled",
fileno(File));
return;
}
setProfileFile(File);
setProfileMergeRequested(EnableMerge);
}
Expand Down Expand Up @@ -347,6 +354,15 @@ static void truncateCurrentFile(void) {
if (lprofCurFilename.MergePoolSize)
return;

/* Only create the profile directory and truncate an existing profile once.
* In continuous mode, this is necessary, as the profile is written-to by the
* runtime initializer. */
const char *lprofInitOnceEnv = "__LLVM_PROFILE_RT_INIT_ONCE";
int initialized = getenv(lprofInitOnceEnv) != NULL;
if (initialized)
return;
setenv(lprofInitOnceEnv, lprofInitOnceEnv, 1);

createProfileDir(Filename);

/* Truncate the file. Later we'll reopen and append. */
Expand All @@ -356,6 +372,99 @@ static void truncateCurrentFile(void) {
fclose(File);
}

static void initializeProfileForContinuousMode(void) {
#if defined(__Fuchsia__) || defined(_WIN32)
PROF_ERR("%s\n", "Continuous mode not yet supported on Fuchsia or Windows.");
#else // defined(__Fuchsia__) || defined(_WIN32)
if (!__llvm_profile_is_continuous_mode_enabled())
return;

/* Get the sizes of various profile data sections. Taken from
* __llvm_profile_get_size_for_buffer(). */
const __llvm_profile_data *DataBegin = __llvm_profile_begin_data();
const __llvm_profile_data *DataEnd = __llvm_profile_end_data();
const uint64_t *CountersBegin = __llvm_profile_begin_counters();
const uint64_t *CountersEnd = __llvm_profile_end_counters();
const char *NamesBegin = __llvm_profile_begin_names();
const char *NamesEnd = __llvm_profile_end_names();
const uint64_t NamesSize = (NamesEnd - NamesBegin) * sizeof(char);
uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd);
uint64_t CountersSize = CountersEnd - CountersBegin;

/* Check that the counter and data sections in this image are page-aligned. */
unsigned PageSize = getpagesize();
if ((intptr_t)CountersBegin % PageSize != 0) {
PROF_ERR("Counters section not page-aligned (start = %p, pagesz = %u).\n",
CountersBegin, PageSize);
return;
}
if ((intptr_t)DataBegin % PageSize != 0) {
PROF_ERR("Data section not page-aligned (start = %p, pagesz = %u).\n",
DataBegin, PageSize);
return;
}

/* Open the raw profile in append mode. */
int Length = getCurFilenameLength();
char *FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1);
const char *Filename = getCurFilename(FilenameBuf, 0);
if (!Filename)
return;
FILE *File = fopen(Filename, "a+b");
if (!File)
return;

int Fileno = fileno(File);

/* Check that the offset within the file is page-aligned. */
off_t CurrentFileOffset = ftello(File);
off_t OffsetModPage = CurrentFileOffset % PageSize;
if (OffsetModPage != 0) {
PROF_ERR("Continuous counter sync mode is enabled, but raw profile is not"
"page-aligned. CurrentFileOffset = %lld, pagesz = %u.\n",
CurrentFileOffset, PageSize);
return;
}

/* Determine how much padding is needed before/after the counters and after
* the names. */
uint64_t PaddingBytesBeforeCounters, PaddingBytesAfterCounters,
PaddingBytesAfterNames;
__llvm_profile_get_padding_sizes_for_counters(
DataSize, CountersSize, NamesSize, &PaddingBytesBeforeCounters,
&PaddingBytesAfterCounters, &PaddingBytesAfterNames);

uint64_t PageAlignedCountersLength =
(CountersSize * sizeof(uint64_t)) + PaddingBytesAfterCounters;
uint64_t FileOffsetToCounters =
CurrentFileOffset + sizeof(__llvm_profile_header) +
(DataSize * sizeof(__llvm_profile_data)) + PaddingBytesBeforeCounters;

/* Write the partial profile. This grows the file to a point where the mmap()
* can succeed. Leak the file handle, as the file should stay open. */
setProfileFile(File);
int rc = writeFile(Filename);
if (rc)
PROF_ERR("Failed to write file \"%s\": %s\n", Filename, strerror(errno));
setProfileFile(NULL);

uint64_t *CounterMmap = (uint64_t *)mmap(
(void *)CountersBegin, PageAlignedCountersLength, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_SHARED, Fileno, FileOffsetToCounters);
if (CounterMmap != CountersBegin) {
PROF_ERR(
"Continuous counter sync mode is enabled, but mmap() failed (%s).\n"
" - CountersBegin: %p\n"
" - PageAlignedCountersLength: %llu\n"
" - Fileno: %d\n"
" - FileOffsetToCounters: %llu\n",
strerror(errno), CountersBegin, PageAlignedCountersLength, Fileno,
FileOffsetToCounters);
return;
}
#endif // defined(__Fuchsia__) || defined(_WIN32)
}

static const char *DefaultProfileName = "default.profraw";
static void resetFilenameToDefault(void) {
if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) {
Expand Down Expand Up @@ -419,12 +528,33 @@ static int parseFilenamePattern(const char *FilenamePat,
FilenamePat);
return -1;
}
} else if (FilenamePat[I] == 'c') {
if (__llvm_profile_is_continuous_mode_enabled()) {
PROF_WARN("%%c specifier can only be specified once in %s.\n",
FilenamePat);
return -1;
}
if (MergingEnabled) {
PROF_WARN("%%c specifier can not be used with profile merging (%%m) "
"in %s.\n",
FilenamePat);
return -1;
}

__llvm_profile_enable_continuous_mode();
I++; /* advance to 'c' */
} else if (containsMergeSpecifier(FilenamePat, I)) {
if (MergingEnabled) {
PROF_WARN("%%m specifier can only be specified once in %s.\n",
FilenamePat);
return -1;
}
if (__llvm_profile_is_continuous_mode_enabled()) {
PROF_WARN("%%c specifier can not be used with profile merging (%%m) "
"in %s.\n",
FilenamePat);
return -1;
}
MergingEnabled = 1;
if (FilenamePat[I] == 'm')
lprofCurFilename.MergePoolSize = 1;
Expand All @@ -447,6 +577,7 @@ static void parseAndSetFilename(const char *FilenamePat,
const char *OldFilenamePat = lprofCurFilename.FilenamePat;
ProfileNameSpecifier OldPNS = lprofCurFilename.PNS;

/* The old profile name specifier takes precedence over the old one. */
if (PNS < OldPNS)
return;

Expand Down Expand Up @@ -475,6 +606,7 @@ static void parseAndSetFilename(const char *FilenamePat,
}

truncateCurrentFile();
initializeProfileForContinuousMode();
}

/* Return buffer length that is required to store the current profile
Expand Down Expand Up @@ -511,7 +643,8 @@ static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf) {
return 0;

if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts ||
lprofCurFilename.MergePoolSize)) {
lprofCurFilename.MergePoolSize ||
__llvm_profile_is_continuous_mode_enabled())) {
if (!ForceUseBuf)
return lprofCurFilename.FilenamePat;

Expand Down Expand Up @@ -646,6 +779,8 @@ void __llvm_profile_initialize_file(void) {
*/
COMPILER_RT_VISIBILITY
void __llvm_profile_set_filename(const char *FilenamePat) {
if (__llvm_profile_is_continuous_mode_enabled())
return;
parseAndSetFilename(FilenamePat, PNS_runtime_api, 1);
}

Expand All @@ -660,7 +795,7 @@ int __llvm_profile_write_file(void) {
char *FilenameBuf;
int PDeathSig = 0;

if (lprofProfileDumped()) {
if (lprofProfileDumped() || __llvm_profile_is_continuous_mode_enabled()) {
PROF_NOTE("Profile data not written to file: %s.\n", "already written");
return 0;
}
Expand Down
10 changes: 10 additions & 0 deletions compiler-rt/lib/profile/InstrProfilingPort.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@
(((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
#endif /* DIR_SEPARATOR_2 */

#if defined(_WIN32)
static inline size_t getpagesize() {
SYSTEM_INFO S;
GetNativeSystemInfo(&S);
return S.dwPageSize;
}
#else /* defined(_WIN32) */
#include <unistd.h>
#endif /* defined(_WIN32) */

#define PROF_ERR(Format, ...) \
fprintf(stderr, "LLVM Profile Error: " Format, __VA_ARGS__);

Expand Down
3 changes: 2 additions & 1 deletion compiler-rt/lib/profile/InstrProfilingRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ namespace {
class RegisterRuntime {
public:
RegisterRuntime() {
__llvm_profile_register_write_file_atexit();
__llvm_profile_initialize_file();
if (!__llvm_profile_is_continuous_mode_enabled())
__llvm_profile_register_write_file_atexit();
}
};

Expand Down
22 changes: 19 additions & 3 deletions compiler-rt/lib/profile/InstrProfilingWriter.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "InstrProfiling.h"
#include "InstrProfilingInternal.h"
#include "InstrProfilingPort.h"

#define INSTR_PROF_VALUE_PROF_DATA
#include "InstrProfData.inc"
Expand Down Expand Up @@ -257,17 +258,26 @@ lprofWriteDataImpl(ProfDataWriter *Writer, const __llvm_profile_data *DataBegin,
const uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd);
const uint64_t CountersSize = CountersEnd - CountersBegin;
const uint64_t NamesSize = NamesEnd - NamesBegin;
const uint64_t Padding = __llvm_profile_get_num_padding_bytes(NamesSize);

/* Enough zeroes for padding. */
const char Zeroes[sizeof(uint64_t)] = {0};
unsigned PageSize = getpagesize();
char Zeroes[PageSize];
memset(Zeroes, 0, PageSize);

/* Create the header. */
__llvm_profile_header Header;

if (!DataSize)
return 0;

/* Determine how much padding is needed before/after the counters and after
* the names. */
uint64_t PaddingBytesBeforeCounters, PaddingBytesAfterCounters,
PaddingBytesAfterNames;
__llvm_profile_get_padding_sizes_for_counters(
DataSize, CountersSize, NamesSize, &PaddingBytesBeforeCounters,
&PaddingBytesAfterCounters, &PaddingBytesAfterNames);

/* Initialize header structure. */
#define INSTR_PROF_RAW_HEADER(Type, Name, Init) Header.Name = Init;
#include "InstrProfData.inc"
Expand All @@ -276,11 +286,17 @@ lprofWriteDataImpl(ProfDataWriter *Writer, const __llvm_profile_data *DataBegin,
ProfDataIOVec IOVec[] = {
{&Header, sizeof(__llvm_profile_header), 1},
{DataBegin, sizeof(__llvm_profile_data), DataSize},
{Zeroes, sizeof(uint8_t), PaddingBytesBeforeCounters},
{CountersBegin, sizeof(uint64_t), CountersSize},
{Zeroes, sizeof(uint8_t), PaddingBytesAfterCounters},
{SkipNameDataWrite ? NULL : NamesBegin, sizeof(uint8_t), NamesSize},
{Zeroes, sizeof(uint8_t), Padding}};
{Zeroes, sizeof(uint8_t), PaddingBytesAfterNames}};
if (Writer->Write(Writer, IOVec, sizeof(IOVec) / sizeof(*IOVec)))
return -1;

/* Value profiling is not yet supported in continuous mode. */
if (__llvm_profile_is_continuous_mode_enabled())
return 0;

return writeValueProfData(Writer, VPDataReader, DataBegin, DataEnd);
}
32 changes: 32 additions & 0 deletions compiler-rt/test/profile/ContinuousSyncMode/basic.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// RUN: %clang -fprofile-instr-generate -fcoverage-mapping -o %t.exe %s
// RUN: echo "garbage" > %t.profraw
// RUN: env LLVM_PROFILE_FILE="%c%t.profraw" %run %t.exe
// RUN: llvm-profdata show --counts --all-functions %t.profraw | FileCheck %s -check-prefix=CHECK-COUNTS
// RUN: llvm-profdata merge -o %t.profdata %t.profraw
// RUN: llvm-cov report %t.exe -instr-profile %t.profdata | FileCheck %s -check-prefix=CHECK-COVERAGE

// CHECK-COUNTS: Counters:
// CHECK-COUNTS-NEXT: main:
// CHECK-COUNTS-NEXT: Hash: 0x{{.*}}
// CHECK-COUNTS-NEXT: Counters: 2
// CHECK-COUNTS-NEXT: Function count: 1
// CHECK-COUNTS-NEXT: Block counts: [1]
// CHECK-COUNTS-NEXT: Instrumentation level: Front-end
// CHECK-COUNTS-NEXT: Functions shown: 1
// CHECK-COUNTS-NEXT: Total functions: 1
// CHECK-COUNTS-NEXT: Maximum function count: 1
// CHECK-COUNTS-NEXT: Maximum internal block count: 1

// CHECK-COVERAGE: Filename Regions Missed Regions Cover Functions Missed Functions Executed Lines Missed Lines Cover
// CHECK-COVERAGE-NEXT: ---
// CHECK-COVERAGE-NEXT: basic.c 4 1 75.00% 1 0 100.00% 5 2 60.00%
// CHECK-COVERAGE-NEXT: ---
// CHECK-COVERAGE-NEXT: TOTAL 4 1 75.00% 1 0 100.00% 5 2 60.00%

extern int __llvm_profile_is_continuous_mode_enabled(void);

int main() {
if (__llvm_profile_is_continuous_mode_enabled())
return 0;
return 1;
}
151 changes: 151 additions & 0 deletions compiler-rt/test/profile/ContinuousSyncMode/darwin-proof-of-concept.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Test whether mmap'ing profile counters onto an open file is feasible. As
// this involves some platform-specific logic, this test is designed to be a
// minimum viable proof-of-concept: it may be useful when porting the mmap()
// mode to a new platform, but is not in and of itself a test of the profiling
// runtime.

// REQUIRES: darwin

// Align counters and data to the maximum expected page size (16K).
// RUN: %clang -g -o %t %s \
// RUN: -Wl,-sectalign,__DATA,__pcnts,0x4000 \
// RUN: -Wl,-sectalign,__DATA,__pdata,0x4000

// Create a 'profile' using mmap() and validate it.
// RUN: %run %t create %t.tmpfile
// RUN: %run %t validate %t.tmpfile

#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

__attribute__((section("__DATA,__pcnts"))) int counters[] = {0xbad};
extern int cnts_start __asm("section$start$__DATA$__pcnts");
const size_t cnts_len = 0x4000;

__attribute__((section("__DATA,__pdata"))) int data[] = {1, 2, 3};
extern int data_start __asm("section$start$__DATA$__pdata");
const size_t data_len = sizeof(int) * 3;

int create_tmpfile(char *path) {
// Create a temp file.
int fd = open(path, O_RDWR | O_TRUNC | O_CREAT, 0666);
if (fd == -1) {
perror("open");
return EXIT_FAILURE;
}

// Grow the file to hold data and counters.
if (0 != ftruncate(fd, cnts_len + data_len)) {
perror("ftruncate");
return EXIT_FAILURE;
}

// Write the data first (at offset 0x4000, after the counters).
if (data_len != pwrite(fd, &data, data_len, 0x4000)) {
perror("write");
return EXIT_FAILURE;
}

// Map the counters into the file, before the data.
//
// Requirements (on Darwin):
// - &cnts_start must be page-aligned.
// - The length and offset-into-fd must be page-aligned.
int *counter_map = (int *)mmap(&cnts_start, 0x4000, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_SHARED, fd, 0);
if (counter_map != &cnts_start) {
perror("mmap");
return EXIT_FAILURE;
}

// Update counters 1..9. These updates should be visible in the file.
// Expect counter 0 (0xbad), which is not updated, to be zero in the file.
for (int i = 1; i < 10; ++i)
counter_map[i] = i;

// Intentionally do not msync(), munmap(), or close().
return EXIT_SUCCESS;
}

int validate_tmpfile(char *path) {
int fd = open(path, O_RDONLY);
if (fd == -1) {
perror("open");
return EXIT_FAILURE;
}

// Verify that the file length is: sizeof(counters) + sizeof(data).
const size_t num_bytes = cnts_len + data_len;
int buf[num_bytes];
if (num_bytes != read(fd, &buf, num_bytes)) {
perror("read");
return EXIT_FAILURE;
}

// Verify the values of counters 1..9 (i.e. that the mmap() worked).
for (int i = 0; i < 10; ++i) {
if (buf[i] != i) {
fprintf(stderr,
"validate_tmpfile: Expected '%d' at pos=%d, but got '%d' instead.\n",
i, i, buf[i]);
return EXIT_FAILURE;
}
}

// Verify that the rest of the counters (after counter 9) are 0.
const int num_cnts = 0x4000 / sizeof(int);
for (int i = 10; i < num_cnts; ++i) {
if (buf[i] != 0) {
fprintf(stderr,
"validate_tmpfile: Expected '%d' at pos=%d, but got '%d' instead.\n",
0, i, buf[i]);
return EXIT_FAILURE;
}
}

// Verify that the data written after the counters is equal to the "data[]"
// array (i.e. {1, 2, 3}).
for (int i = num_cnts; i < num_cnts + 3; ++i) {
if (buf[i] != (i - num_cnts + 1)) {
fprintf(stderr,
"validate_tmpfile: Expected '%d' at pos=%d, but got '%d' instead.\n",
i - num_cnts + 1, i, buf[i]);
return EXIT_FAILURE;
}
}

// Intentionally do not close().
return EXIT_SUCCESS;
}

int main(int argc, char **argv) {
intptr_t cnts_start_int = (intptr_t)&cnts_start;
intptr_t data_start_int = (intptr_t)&data_start;
int pagesz = getpagesize();

if (cnts_start_int % pagesz != 0) {
fprintf(stderr, "__pcnts is not page-aligned: 0x%lx.\n", cnts_start_int);
return EXIT_FAILURE;
}
if (data_start_int % pagesz != 0) {
fprintf(stderr, "__pdata is not page-aligned: 0x%lx.\n", data_start_int);
return EXIT_FAILURE;
}
if (cnts_start_int + 0x4000 != data_start_int) {
fprintf(stderr, "__pdata not ordered after __pcnts.\n");
return EXIT_FAILURE;
}

char *action = argv[1];
char *path = argv[2];
if (0 == strcmp(action, "create"))
return create_tmpfile(path);
else if (0 == strcmp(action, "validate"))
return validate_tmpfile(path);
else
return EXIT_FAILURE;
}
18 changes: 18 additions & 0 deletions compiler-rt/test/profile/ContinuousSyncMode/lit.local.cfg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import subprocess

def getRoot(config):
if not config.parent:
return config
return getRoot(config.parent)

root = getRoot(config)

# As this has not been tested extensively on non-Darwin platforms,
# only Darwin support is enabled for the moment. However, continuous mode
# may "just work" without modification on Linux and other UNIX-likes (AIUI
# the default value for the GNU linker's `--section-alignment` flag is
# 0x1000, which is the size of a page on many systems).
#
# Please add supported configs to this list.
if root.host_os not in ['Darwin']:
config.unsupported = True
35 changes: 35 additions & 0 deletions compiler-rt/test/profile/ContinuousSyncMode/multiple-DSOs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// RUN: echo "void dso1(void) {}" > %t.dso1.c
// RUN: echo "void dso2(void) {}" > %t.dso2.c
// RUN: %clang_pgogen -dynamiclib -o %t.dso1.dylib %t.dso1.c
// RUN: %clang_pgogen -dynamiclib -o %t.dso2.dylib %t.dso2.c
// RUN: %clang_pgogen -o %t.exe %s %t.dso1.dylib %t.dso2.dylib
// RUN: env LLVM_PROFILE_FILE="%c%t.profraw" %run %t.exe
// RUN: llvm-profdata show --counts --all-functions %t.profraw | FileCheck %s

// CHECK-LABEL: Counters:
// CHECK-NEXT: dso1:
// CHECK-NEXT: Hash: 0x{{.*}}
// CHECK-NEXT: Counters: 1
// CHECK-NEXT: Block counts: [1]
// CHECK-NEXT: dso2:
// CHECK-NEXT: Hash: 0x{{.*}}
// CHECK-NEXT: Counters: 1
// CHECK-NEXT: Block counts: [1]
// CHECK-NEXT: main:
// CHECK-NEXT: Hash: 0x{{.*}}
// CHECK-NEXT: Counters: 1
// CHECK-NEXT: Block counts: [1]
// CHECK-NEXT: Instrumentation level: IR
// CHECK-NEXT: Functions shown: 3
// CHECK-NEXT: Total functions: 3
// CHECK-NEXT: Maximum function count: 1
// CHECK-NEXT: Maximum internal block count: 0

void dso1(void);
void dso2(void);

int main() {
dso1();
dso2();
return 0;
}
34 changes: 34 additions & 0 deletions compiler-rt/test/profile/ContinuousSyncMode/pid-substitution.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// RUN: rm -rf %t.dir && mkdir -p %t.dir
// RUN: %clang_pgogen -o %t.exe %s
//
// Note: %%p is needed here, not %p, because of lit's path substitution.
// RUN: env LLVM_PROFILE_FILE="%t.dir/%c-%%p" %run %t.exe

#include <stdlib.h>
#include <string.h>

extern int __llvm_profile_is_continuous_mode_enabled(void);
extern const char *__llvm_profile_get_filename(void);
extern int getpid(void);

int main() {
// Check that continuous mode is enabled.
if (!__llvm_profile_is_continuous_mode_enabled())
return 1;

// Check that the PID is actually in the filename.
const char *Filename = __llvm_profile_get_filename();

int Len = strlen(Filename);
--Len;
while (Filename[Len] != '-')
--Len;

const char *PidStr = Filename + Len + 1;
int Pid = atoi(PidStr);

if (Pid != getpid())
return 1;

return 0;
}
32 changes: 32 additions & 0 deletions compiler-rt/test/profile/ContinuousSyncMode/set-file-object.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// RUN: %clang_pgogen -o %t.exe %s
// RUN: env LLVM_PROFILE_FILE="%c%t.profraw" %run %t.exe %t.bad 2>&1 | FileCheck %s

// CHECK: __llvm_profile_set_file_object(fd={{[0-9]+}}) not supported
// CHECK: Profile data not written to file: already written.

#include <stdio.h>

extern int __llvm_profile_is_continuous_mode_enabled(void);
extern void __llvm_profile_set_file_object(FILE *, int);
extern int __llvm_profile_write_file(void);

int main(int argc, char **argv) {
if (!__llvm_profile_is_continuous_mode_enabled())
return 1;

FILE *f = fopen(argv[1], "a+b");
if (!f)
return 1;

__llvm_profile_set_file_object(f, 0); // Try to set the file to "%t.bad".

if (__llvm_profile_write_file() != 0)
return 1;

f = fopen(argv[1], "r");
if (!f)
return 1;

fseek(f, 0, SEEK_END);
return ftell(f); // Check that the "%t.bad" is empty.
}
17 changes: 17 additions & 0 deletions compiler-rt/test/profile/ContinuousSyncMode/set-filename.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// RUN: %clang_pgogen -o %t.exe %s
// RUN: env LLVM_PROFILE_FILE="%c%t.profraw" %run %t.exe %t.profraw %t.bad

#include <string.h>

extern int __llvm_profile_is_continuous_mode_enabled(void);
extern void __llvm_profile_set_filename(const char *);
extern const char *__llvm_profile_get_filename();

int main(int argc, char **argv) {
if (!__llvm_profile_is_continuous_mode_enabled())
return 1;

__llvm_profile_set_filename(argv[2]); // Try to set the filename to "%t.bad".
const char *Filename = __llvm_profile_get_filename();
return strcmp(Filename, argv[1]); // Check that the filename is "%t.profraw".
}
1 change: 1 addition & 0 deletions compiler-rt/test/profile/instrprof-write-file.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// RUN: rm -rf %t1.profraw %t2.profraw
// RUN: %clang_profgen -o %t -O3 %s
// RUN: env LLVM_PROFILE_FILE=%t1.profraw %run %t %t2.profraw
// RUN: llvm-profdata merge -o %t1.profdata %t1.profraw
Expand Down
6 changes: 4 additions & 2 deletions llvm/include/llvm/ProfileData/InstrProfData.inc
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,9 @@ INSTR_PROF_VALUE_NODE(PtrToNodeT, llvm::Type::getInt8PtrTy(Ctx), Next, \
INSTR_PROF_RAW_HEADER(uint64_t, Magic, __llvm_profile_get_magic())
INSTR_PROF_RAW_HEADER(uint64_t, Version, __llvm_profile_get_version())
INSTR_PROF_RAW_HEADER(uint64_t, DataSize, DataSize)
INSTR_PROF_RAW_HEADER(uint64_t, PaddingBytesBeforeCounters, PaddingBytesBeforeCounters)
INSTR_PROF_RAW_HEADER(uint64_t, CountersSize, CountersSize)
INSTR_PROF_RAW_HEADER(uint64_t, PaddingBytesAfterCounters, PaddingBytesAfterCounters)
INSTR_PROF_RAW_HEADER(uint64_t, NamesSize, NamesSize)
INSTR_PROF_RAW_HEADER(uint64_t, CountersDelta, (uintptr_t)CountersBegin)
INSTR_PROF_RAW_HEADER(uint64_t, NamesDelta, (uintptr_t)NamesBegin)
Expand Down Expand Up @@ -628,7 +630,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
(uint64_t)'f' << 16 | (uint64_t)'R' << 8 | (uint64_t)129

/* Raw profile format version (start from 1). */
#define INSTR_PROF_RAW_VERSION 4
#define INSTR_PROF_RAW_VERSION 5
/* Indexed profile format version (start from 1). */
#define INSTR_PROF_INDEX_VERSION 5
/* Coverage mapping format vresion (start from 0). */
Expand Down Expand Up @@ -742,7 +744,7 @@ typedef struct InstrProfValueData {
#endif /* INSTR_PROF_DATA_INC */

#ifndef INSTR_ORDER_FILE_INC
// The maximal # of functions: 128*1024 (the buffer size will be 128*4 KB).
/* The maximal # of functions: 128*1024 (the buffer size will be 128*4 KB). */
#define INSTR_ORDER_FILE_BUFFER_SIZE 131072
#define INSTR_ORDER_FILE_BUFFER_BITS 17
#define INSTR_ORDER_FILE_BUFFER_MASK 0x1ffff
Expand Down
8 changes: 6 additions & 2 deletions llvm/lib/ProfileData/InstrProfReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,16 +362,20 @@ Error RawInstrProfReader<IntPtrT>::readHeader(
CountersDelta = swap(Header.CountersDelta);
NamesDelta = swap(Header.NamesDelta);
auto DataSize = swap(Header.DataSize);
auto PaddingBytesBeforeCounters = swap(Header.PaddingBytesBeforeCounters);
auto CountersSize = swap(Header.CountersSize);
auto PaddingBytesAfterCounters = swap(Header.PaddingBytesAfterCounters);
NamesSize = swap(Header.NamesSize);
ValueKindLast = swap(Header.ValueKindLast);

auto DataSizeInBytes = DataSize * sizeof(RawInstrProf::ProfileData<IntPtrT>);
auto PaddingSize = getNumPaddingBytes(NamesSize);

ptrdiff_t DataOffset = sizeof(RawInstrProf::Header);
ptrdiff_t CountersOffset = DataOffset + DataSizeInBytes;
ptrdiff_t NamesOffset = CountersOffset + sizeof(uint64_t) * CountersSize;
ptrdiff_t CountersOffset =
DataOffset + DataSizeInBytes + PaddingBytesBeforeCounters;
ptrdiff_t NamesOffset = CountersOffset + (sizeof(uint64_t) * CountersSize) +
PaddingBytesAfterCounters;
ptrdiff_t ValueDataOffset = NamesOffset + NamesSize + PaddingSize;

auto *Start = reinterpret_cast<const char *>(&Header);
Expand Down
Binary file modified llvm/test/tools/llvm-profdata/Inputs/c-general.profraw
Binary file not shown.
4 changes: 2 additions & 2 deletions llvm/test/tools/llvm-profdata/c-general.test
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ $ SRC=path/to/llvm
$ CFE=$SRC/tools/clang
$ TESTDIR=$SRC/test/tools/llvm-profdata
$ CFE_TESTDIR=$CFE/test/Profile
$ clang -o a.out -fprofile-instr-generate $CFE_TESTDIR/c-general.c
$ clang -o a.out -fprofile-instr-generate $CFE_TESTDIR/c-general.c -mllvm -enable-name-compression=false
$ LLVM_PROFILE_FILE=$TESTDIR/Inputs/c-general.profraw ./a.out

RUN: llvm-profdata show %p/Inputs/c-general.profraw -o - | FileCheck %s
Expand All @@ -14,7 +14,7 @@ RUN: llvm-profdata show %p/Inputs/c-general.profraw -o - --function=switches | F

SWITCHES-LABEL: Counters:
SWITCHES-NEXT: switches:
SWITCHES-NEXT: Hash: 0x2618e4f23f2e8daa
SWITCHES-NEXT: Hash: 0xa50a07f391ae4be5
SWITCHES-NEXT: Counters: 19
SWITCHES-NEXT: Function count: 1
SWITCHES-LABEL: Functions shown: 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
// INSTR_PROF_RAW_HEADER(uint64_t, ValueKindLast, IPVK_Last)

RUN: printf '\201rforpl\377' > %t.profraw
RUN: printf '\4\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\5\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\1\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\2\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\10\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\0\0\6\0\1\0\0\0' >> %t.profraw
RUN: printf '\0\0\6\0\2\0\0\0' >> %t.profraw
Expand Down
4 changes: 3 additions & 1 deletion llvm/test/tools/llvm-profdata/raw-32-bits-be.test
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
RUN: printf '\377lprofR\201' > %t
RUN: printf '\0\0\0\0\0\0\0\4' >> %t
RUN: printf '\0\0\0\0\0\0\0\5' >> %t
RUN: printf '\0\0\0\0\0\0\0\2' >> %t
RUN: printf '\0\0\0\0\0\0\0\0' >> %t
RUN: printf '\0\0\0\0\0\0\0\3' >> %t
RUN: printf '\0\0\0\0\0\0\0\0' >> %t
RUN: printf '\0\0\0\0\0\0\0\20' >> %t
RUN: printf '\0\0\0\0\1\0\0\0' >> %t
RUN: printf '\0\0\0\0\2\0\0\0' >> %t
Expand Down
4 changes: 3 additions & 1 deletion llvm/test/tools/llvm-profdata/raw-32-bits-le.test
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
RUN: printf '\201Rforpl\377' > %t
RUN: printf '\4\0\0\0\0\0\0\0' >> %t
RUN: printf '\5\0\0\0\0\0\0\0' >> %t
RUN: printf '\2\0\0\0\0\0\0\0' >> %t
RUN: printf '\0\0\0\0\0\0\0\0' >> %t
RUN: printf '\3\0\0\0\0\0\0\0' >> %t
RUN: printf '\0\0\0\0\0\0\0\0' >> %t
RUN: printf '\20\0\0\0\0\0\0\0' >> %t
RUN: printf '\0\0\0\1\0\0\0\0' >> %t
RUN: printf '\0\0\0\2\0\0\0\0' >> %t
Expand Down
4 changes: 3 additions & 1 deletion llvm/test/tools/llvm-profdata/raw-64-bits-be.test
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
RUN: printf '\377lprofr\201' > %t
RUN: printf '\0\0\0\0\0\0\0\4' >> %t
RUN: printf '\0\0\0\0\0\0\0\5' >> %t
RUN: printf '\0\0\0\0\0\0\0\2' >> %t
RUN: printf '\0\0\0\0\0\0\0\0' >> %t
RUN: printf '\0\0\0\0\0\0\0\3' >> %t
RUN: printf '\0\0\0\0\0\0\0\0' >> %t
RUN: printf '\0\0\0\0\0\0\0\20' >> %t
RUN: printf '\0\0\0\1\0\4\0\0' >> %t
RUN: printf '\0\0\0\2\0\4\0\0' >> %t
Expand Down
4 changes: 3 additions & 1 deletion llvm/test/tools/llvm-profdata/raw-64-bits-le.test
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
RUN: printf '\201rforpl\377' > %t
RUN: printf '\4\0\0\0\0\0\0\0' >> %t
RUN: printf '\5\0\0\0\0\0\0\0' >> %t
RUN: printf '\2\0\0\0\0\0\0\0' >> %t
RUN: printf '\0\0\0\0\0\0\0\0' >> %t
RUN: printf '\3\0\0\0\0\0\0\0' >> %t
RUN: printf '\0\0\0\0\0\0\0\0' >> %t
RUN: printf '\20\0\0\0\0\0\0\0' >> %t
RUN: printf '\0\0\4\0\1\0\0\0' >> %t
RUN: printf '\0\0\4\0\2\0\0\0' >> %t
Expand Down
8 changes: 6 additions & 2 deletions llvm/test/tools/llvm-profdata/raw-two-profiles.test
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
RUN: printf '\201rforpl\377' > %t-foo.profraw
RUN: printf '\4\0\0\0\0\0\0\0' >> %t-foo.profraw
RUN: printf '\5\0\0\0\0\0\0\0' >> %t-foo.profraw
RUN: printf '\1\0\0\0\0\0\0\0' >> %t-foo.profraw
RUN: printf '\0\0\0\0\0\0\0\0' >> %t-foo.profraw
RUN: printf '\1\0\0\0\0\0\0\0' >> %t-foo.profraw
RUN: printf '\0\0\0\0\0\0\0\0' >> %t-foo.profraw
RUN: printf '\10\0\0\0\0\0\0\0' >> %t-foo.profraw
RUN: printf '\0\0\4\0\1\0\0\0' >> %t-foo.profraw
RUN: printf '\0\0\4\0\2\0\0\0' >> %t-foo.profraw
Expand All @@ -18,9 +20,11 @@ RUN: printf '\023\0\0\0\0\0\0\0' >> %t-foo.profraw
RUN: printf '\3\0foo\0\0\0' >> %t-foo.profraw

RUN: printf '\201rforpl\377' > %t-bar.profraw
RUN: printf '\4\0\0\0\0\0\0\0' >> %t-bar.profraw
RUN: printf '\5\0\0\0\0\0\0\0' >> %t-bar.profraw
RUN: printf '\1\0\0\0\0\0\0\0' >> %t-bar.profraw
RUN: printf '\0\0\0\0\0\0\0\0' >> %t-bar.profraw
RUN: printf '\2\0\0\0\0\0\0\0' >> %t-bar.profraw
RUN: printf '\0\0\0\0\0\0\0\0' >> %t-bar.profraw
RUN: printf '\10\0\0\0\0\0\0\0' >> %t-bar.profraw
RUN: printf '\0\0\6\0\1\0\0\0' >> %t-bar.profraw
RUN: printf '\0\0\6\0\2\0\0\0' >> %t-bar.profraw
Expand Down