[compiler-rt] d889d1e - [profile] Add a mode to continuously sync counter updates to a file
Vedant Kumar via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 31 16:08:40 PDT 2019
Author: Vedant Kumar
Date: 2019-10-31T16:04:09-07:00
New Revision: d889d1efefe9f97507e3eafa85a2e3939df9750f
URL: https://github.com/llvm/llvm-project/commit/d889d1efefe9f97507e3eafa85a2e3939df9750f
DIFF: https://github.com/llvm/llvm-project/commit/d889d1efefe9f97507e3eafa85a2e3939df9750f.diff
LOG: [profile] Add a mode to continuously sync counter updates to a file
Add support for continuously syncing profile counter updates to a file.
The motivation for this is that programs do not always exit cleanly. On
iOS, for example, programs are usually killed via a signal from the OS.
Running atexit() handlers after catching a signal is unreliable, so some
method for progressively writing out profile data is necessary.
The approach taken here is to mmap() the `__llvm_prf_cnts` section onto
a raw profile. To do this, the linker must page-align the counter and
data sections, and the runtime must ensure that counters are mapped to a
page-aligned offset within a raw profile.
Continuous mode is (for the moment) incompatible with the online merging
mode. This limitation is lifted in https://reviews.llvm.org/D69586.
Continuous mode is also (for the moment) incompatible with value
profiling, as I'm not sure whether there is interest in this and the
implementation may be tricky.
As I have not been able to test extensively on non-Darwin platforms,
only Darwin support is included for the moment. However, continuous mode
may "just work" without modification on Linux and some UNIX-likes. AIUI
the default value for the GNU linker's `--section-alignment` flag is set
to the page size on many systems. This appears to be true for LLD as
well, as its `no_nmagic` option is on by default. Continuous mode will
not "just work" on Fuchsia or Windows, as it's not possible to mmap() a
section on these platforms. There is a proposal to add a layer of
indirection to the profile instrumentation to support these platforms.
rdar://54210980
Differential Revision: https://reviews.llvm.org/D68351
Added:
compiler-rt/test/profile/ContinuousSyncMode/basic.c
compiler-rt/test/profile/ContinuousSyncMode/darwin-proof-of-concept.c
compiler-rt/test/profile/ContinuousSyncMode/lit.local.cfg.py
compiler-rt/test/profile/ContinuousSyncMode/multiple-DSOs.c
compiler-rt/test/profile/ContinuousSyncMode/pid-substitution.c
compiler-rt/test/profile/ContinuousSyncMode/set-file-object.c
compiler-rt/test/profile/ContinuousSyncMode/set-filename.c
Modified:
clang/docs/SourceBasedCodeCoverage.rst
clang/lib/Driver/ToolChains/Darwin.cpp
clang/test/Driver/darwin-ld.c
compiler-rt/lib/profile/InstrProfData.inc
compiler-rt/lib/profile/InstrProfiling.h
compiler-rt/lib/profile/InstrProfilingBuffer.c
compiler-rt/lib/profile/InstrProfilingFile.c
compiler-rt/lib/profile/InstrProfilingPort.h
compiler-rt/lib/profile/InstrProfilingRuntime.cpp
compiler-rt/lib/profile/InstrProfilingWriter.c
llvm/include/llvm/ProfileData/InstrProfData.inc
llvm/lib/ProfileData/InstrProfReader.cpp
llvm/test/tools/llvm-profdata/Inputs/c-general.profraw
llvm/test/tools/llvm-profdata/c-general.test
llvm/test/tools/llvm-profdata/malformed-ptr-to-counter-array.test
llvm/test/tools/llvm-profdata/raw-32-bits-be.test
llvm/test/tools/llvm-profdata/raw-32-bits-le.test
llvm/test/tools/llvm-profdata/raw-64-bits-be.test
llvm/test/tools/llvm-profdata/raw-64-bits-le.test
llvm/test/tools/llvm-profdata/raw-two-profiles.test
Removed:
################################################################################
diff --git a/clang/docs/SourceBasedCodeCoverage.rst b/clang/docs/SourceBasedCodeCoverage.rst
index 27a1950a1602..0beb284e475e 100644
--- a/clang/docs/SourceBasedCodeCoverage.rst
+++ b/clang/docs/SourceBasedCodeCoverage.rst
@@ -87,6 +87,16 @@ directory structure will be created. Additionally, the following special
be between 1 and 9. The merge pool specifier can only occur once per filename
pattern.
+* "%c" expands out to nothing, but enables a mode in which profile counter
+ updates are continuously synced to a file. This means that if the
+ instrumented program crashes, or is killed by a signal, perfect coverage
+ information can still be recovered. Continuous mode is not yet compatible with
+ the "%Nm" merging mode described above, does not support value profiling for
+ PGO, and is only supported on Darwin. Support for Linux may be mostly
+ complete but requires testing, and support for Fuchsia/Windows may require
+ more extensive changes: please get involved if you are interested in porting
+ this feature.
+
.. code-block:: console
# Step 2: Run the program.
diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp
index e15f089b0d7e..d8c18effd62c 100644
--- a/clang/lib/Driver/ToolChains/Darwin.cpp
+++ b/clang/lib/Driver/ToolChains/Darwin.cpp
@@ -19,6 +19,7 @@
#include "clang/Driver/SanitizerArgs.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Option/ArgList.h"
+#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/TargetParser.h"
@@ -1110,6 +1111,19 @@ static void addExportedSymbol(ArgStringList &CmdArgs, const char *Symbol) {
CmdArgs.push_back(Symbol);
}
+/// Add a sectalign directive for \p Segment and \p Section to the maximum
+/// expected page size for Darwin.
+///
+/// On iPhone 6+ the max supported page size is 16K. On macOS, the max is 4K.
+/// Use a common alignment constant (16K) for now, and reduce the alignment on
+/// macOS if it proves important.
+static void addSectalignToPage(const ArgList &Args, ArgStringList &CmdArgs,
+ StringRef Segment, StringRef Section) {
+ for (const char *A : {"-sectalign", Args.MakeArgString(Segment),
+ Args.MakeArgString(Section), "0x4000"})
+ CmdArgs.push_back(A);
+}
+
void Darwin::addProfileRTLibs(const ArgList &Args,
ArgStringList &CmdArgs) const {
if (!needsProfileRT(Args)) return;
@@ -1117,11 +1131,13 @@ void Darwin::addProfileRTLibs(const ArgList &Args,
AddLinkRuntimeLib(Args, CmdArgs, "profile",
RuntimeLinkOptions(RLO_AlwaysLink | RLO_FirstLink));
+ bool ForGCOV = needsGCovInstrumentation(Args);
+
// If we have a symbol export directive and we're linking in the profile
// runtime, automatically export symbols necessary to implement some of the
// runtime's functionality.
if (hasExportSymbolDirective(Args)) {
- if (needsGCovInstrumentation(Args)) {
+ if (ForGCOV) {
addExportedSymbol(CmdArgs, "___gcov_flush");
addExportedSymbol(CmdArgs, "_flush_fn_list");
addExportedSymbol(CmdArgs, "_writeout_fn_list");
@@ -1131,6 +1147,24 @@ void Darwin::addProfileRTLibs(const ArgList &Args,
}
addExportedSymbol(CmdArgs, "_lprofDirMode");
}
+
+ // Align __llvm_prf_{cnts,data} sections to the maximum expected page
+ // alignment. This allows profile counters to be mmap()'d to disk. Note that
+ // it's not enough to just page-align __llvm_prf_cnts: the following section
+ // must also be page-aligned so that its data is not clobbered by mmap().
+ //
+ // The section alignment is only needed when continuous profile sync is
+ // enabled, but this is expected to be the default in Xcode. Specifying the
+ // extra alignment also allows the same binary to be used with/without sync
+ // enabled.
+ if (!ForGCOV) {
+ for (auto IPSK : {llvm::IPSK_cnts, llvm::IPSK_data}) {
+ addSectalignToPage(
+ Args, CmdArgs, "__DATA",
+ llvm::getInstrProfSectionName(IPSK, llvm::Triple::MachO,
+ /*AddSegmentInfo=*/false));
+ }
+ }
}
void DarwinClang::AddLinkSanitizerLibArgs(const ArgList &Args,
diff --git a/clang/test/Driver/darwin-ld.c b/clang/test/Driver/darwin-ld.c
index 18a1fd9afc55..420dda357f8b 100644
--- a/clang/test/Driver/darwin-ld.c
+++ b/clang/test/Driver/darwin-ld.c
@@ -345,6 +345,12 @@
// RUN: FileCheck -check-prefix=LINK_PROFILE_FIRST %s < %t.log
// LINK_PROFILE_FIRST: {{ld(.exe)?"}} "{{[^"]+}}libclang_rt.profile_{{[a-z]+}}.a"
+// RUN: %clang -target x86_64-apple-darwin12 -fprofile-instr-generate -### %t.o 2> %t.log
+// RUN: FileCheck -check-prefix=PROFILE_SECTALIGN %s < %t.log
+// RUN: %clang -target arm64-apple-ios12 -fprofile-instr-generate -### %t.o 2> %t.log
+// RUN: FileCheck -check-prefix=PROFILE_SECTALIGN %s < %t.log
+// PROFILE_SECTALIGN: "-sectalign" "__DATA" "__llvm_prf_cnts" "0x4000" "-sectalign" "__DATA" "__llvm_prf_data" "0x4000"
+
// RUN: %clang -target x86_64-apple-darwin12 -fprofile-instr-generate -exported_symbols_list /dev/null -### %t.o 2> %t.log
// RUN: FileCheck -check-prefix=PROFILE_EXPORT %s < %t.log
// RUN: %clang -target x86_64-apple-darwin12 -fprofile-instr-generate -Wl,-exported_symbols_list,/dev/null -### %t.o 2> %t.log
diff --git a/compiler-rt/lib/profile/InstrProfData.inc b/compiler-rt/lib/profile/InstrProfData.inc
index 7078af5f4cf8..99f41d8fef07 100644
--- a/compiler-rt/lib/profile/InstrProfData.inc
+++ b/compiler-rt/lib/profile/InstrProfData.inc
@@ -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)
@@ -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). */
diff --git a/compiler-rt/lib/profile/InstrProfiling.h b/compiler-rt/lib/profile/InstrProfiling.h
index ffc4396169d0..78dfc675efd7 100644
--- a/compiler-rt/lib/profile/InstrProfiling.h
+++ b/compiler-rt/lib/profile/InstrProfiling.h
@@ -38,6 +38,22 @@ typedef struct ValueProfNode {
#include "InstrProfData.inc"
} ValueProfNode;
+/*!
+ * \brief Return 1 if profile counters are continuously synced to the raw
+ * profile via an mmap(). This is in contrast to the default mode, in which
+ * the raw profile is written out at program exit time.
+ */
+int __llvm_profile_is_continuous_mode_enabled(void);
+
+/*!
+ * \brief Enable continuous mode.
+ *
+ * See \ref __llvm_profile_is_continuous_mode_enabled. The behavior is undefined
+ * if continuous mode is already enabled, or if it cannot be enable due to
+ * conflicting options.
+ */
+void __llvm_profile_enable_continuous_mode(void);
+
/*!
* \brief Get number of bytes necessary to pad the argument to eight
* byte boundary.
@@ -159,6 +175,12 @@ int __llvm_orderfile_dump(void);
* Note: There may be multiple copies of the profile runtime (one for each
* instrumented image/DSO). This API only modifies the filename within the
* copy of the runtime available to the calling image.
+ *
+ * Warning: This is a no-op if continuous mode (\ref
+ * __llvm_profile_is_continuous_mode_enabled) is on. The reason for this is
+ * that in continuous mode, profile counters are mmap()'d to the profile at
+ * program initialization time. Support for transferring the mmap'd profile
+ * counts to a new file has not been implemented.
*/
void __llvm_profile_set_filename(const char *Name);
@@ -181,6 +203,12 @@ void __llvm_profile_set_filename(const char *Name);
* Note: There may be multiple copies of the profile runtime (one for each
* instrumented image/DSO). This API only modifies the file object within the
* copy of the runtime available to the calling image.
+ *
+ * Warning: This is a no-op if continuous mode (\ref
+ * __llvm_profile_is_continuous_mode_enabled) is on. The reason for this is
+ * that in continuous mode, profile counters are mmap()'d to the profile at
+ * program initialization time. Support for transferring the mmap'd profile
+ * counts to a new file has not been implemented.
*/
void __llvm_profile_set_file_object(FILE *File, int EnableMerge);
@@ -223,6 +251,24 @@ uint64_t __llvm_profile_get_version(void);
uint64_t __llvm_profile_get_data_size(const __llvm_profile_data *Begin,
const __llvm_profile_data *End);
+/* ! \brief Given the sizes of the data and counter information, return the
+ * number of padding bytes before and after the counters, and after the names,
+ * in the raw profile.
+ *
+ * Note: In this context, "size" means "number of entries", i.e. the first two
+ * arguments must be the result of __llvm_profile_get_data_size() and of
+ * (__llvm_profile_end_counters() - __llvm_profile_begin_counters()) resp.
+ *
+ * Note: When mmap() mode is disabled, no padding bytes before/after counters
+ * are needed. However, in mmap() mode, the counter section in the raw profile
+ * must be page-aligned: this API computes the number of padding bytes
+ * needed to achieve that.
+ */
+void __llvm_profile_get_padding_sizes_for_counters(
+ uint64_t DataSize, uint64_t CountersSize, uint64_t NamesSize,
+ uint64_t *PaddingBytesBeforeCounters, uint64_t *PaddingBytesAfterCounters,
+ uint64_t *PaddingBytesAfterNames);
+
/*!
* \brief Set the flag that profile data has been dumped to the file.
* This is useful for users to disable dumping profile data to the file for
diff --git a/compiler-rt/lib/profile/InstrProfilingBuffer.c b/compiler-rt/lib/profile/InstrProfilingBuffer.c
index 5bdeb8e32807..089ff5a0153d 100644
--- a/compiler-rt/lib/profile/InstrProfilingBuffer.c
+++ b/compiler-rt/lib/profile/InstrProfilingBuffer.c
@@ -8,6 +8,27 @@
#include "InstrProfiling.h"
#include "InstrProfilingInternal.h"
+#include "InstrProfilingPort.h"
+
+/* When continuous mode is enabled (%c), this parameter is set to 1. This is
+ * incompatible with the in-process merging mode. Lifting this restriction
+ * may be complicated, as merging mode requires a lock on the profile, and
+ * mmap() mode would require that lock to be held for the entire process
+ * lifetime.
+ *
+ * This parameter is defined here in InstrProfilingBuffer.o, instead of in
+ * InstrProfilingFile.o, to sequester all libc-dependent code in
+ * InstrProfilingFile.o. The test `instrprof-without-libc` will break if this
+ * layering is violated. */
+static int ContinuouslySyncProfile = 0;
+
+COMPILER_RT_VISIBILITY int __llvm_profile_is_continuous_mode_enabled(void) {
+ return ContinuouslySyncProfile;
+}
+
+COMPILER_RT_VISIBILITY void __llvm_profile_enable_continuous_mode(void) {
+ ContinuouslySyncProfile = 1;
+}
COMPILER_RT_VISIBILITY
uint64_t __llvm_profile_get_size_for_buffer(void) {
@@ -30,6 +51,41 @@ uint64_t __llvm_profile_get_data_size(const __llvm_profile_data *Begin,
sizeof(__llvm_profile_data);
}
+/// Calculate the number of padding bytes needed to add to \p Offset in order
+/// for (\p Offset + Padding) to be page-aligned.
+static uint64_t calculateBytesNeededToPageAlign(uint64_t Offset,
+ unsigned PageSize) {
+ uint64_t OffsetModPage = Offset % PageSize;
+ if (OffsetModPage > 0)
+ return PageSize - OffsetModPage;
+ return 0;
+}
+
+COMPILER_RT_VISIBILITY
+void __llvm_profile_get_padding_sizes_for_counters(
+ uint64_t DataSize, uint64_t CountersSize, uint64_t NamesSize,
+ uint64_t *PaddingBytesBeforeCounters, uint64_t *PaddingBytesAfterCounters,
+ uint64_t *PaddingBytesAfterNames) {
+ if (!__llvm_profile_is_continuous_mode_enabled()) {
+ *PaddingBytesBeforeCounters = 0;
+ *PaddingBytesAfterCounters = 0;
+ *PaddingBytesAfterNames = __llvm_profile_get_num_padding_bytes(NamesSize);
+ return;
+ }
+
+ // In continuous mode, the file offsets for headers and for the start of
+ // counter sections need to be page-aligned.
+ unsigned PageSize = getpagesize();
+ uint64_t DataSizeInBytes = DataSize * sizeof(__llvm_profile_data);
+ uint64_t CountersSizeInBytes = CountersSize * sizeof(uint64_t);
+ *PaddingBytesBeforeCounters = calculateBytesNeededToPageAlign(
+ sizeof(__llvm_profile_header) + DataSizeInBytes, PageSize);
+ *PaddingBytesAfterCounters =
+ calculateBytesNeededToPageAlign(CountersSizeInBytes, PageSize);
+ *PaddingBytesAfterNames =
+ calculateBytesNeededToPageAlign(NamesSize, PageSize);
+}
+
COMPILER_RT_VISIBILITY
uint64_t __llvm_profile_get_size_for_buffer_internal(
const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd,
@@ -37,11 +93,21 @@ uint64_t __llvm_profile_get_size_for_buffer_internal(
const char *NamesBegin, const char *NamesEnd) {
/* Match logic in __llvm_profile_write_buffer(). */
const uint64_t NamesSize = (NamesEnd - NamesBegin) * sizeof(char);
- const uint8_t Padding = __llvm_profile_get_num_padding_bytes(NamesSize);
+ uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd);
+ uint64_t CountersSize = CountersEnd - CountersBegin;
+
+ /* 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);
+
return sizeof(__llvm_profile_header) +
- (__llvm_profile_get_data_size(DataBegin, DataEnd) *
- sizeof(__llvm_profile_data)) +
- (CountersEnd - CountersBegin) * sizeof(uint64_t) + NamesSize + Padding;
+ (DataSize * sizeof(__llvm_profile_data)) + PaddingBytesBeforeCounters +
+ (CountersSize * sizeof(uint64_t)) + PaddingBytesAfterCounters +
+ NamesSize + PaddingBytesAfterNames;
}
COMPILER_RT_VISIBILITY
diff --git a/compiler-rt/lib/profile/InstrProfilingFile.c b/compiler-rt/lib/profile/InstrProfilingFile.c
index 1b253c3e865e..e2253de8eda2 100644
--- a/compiler-rt/lib/profile/InstrProfilingFile.c
+++ b/compiler-rt/lib/profile/InstrProfilingFile.c
@@ -32,6 +32,7 @@
#include "InstrProfiling.h"
#include "InstrProfilingInternal.h"
+#include "InstrProfilingPort.h"
#include "InstrProfilingUtil.h"
/* From where is profile name specified.
@@ -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);
}
@@ -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. */
@@ -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) {
@@ -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;
@@ -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;
@@ -475,6 +606,7 @@ static void parseAndSetFilename(const char *FilenamePat,
}
truncateCurrentFile();
+ initializeProfileForContinuousMode();
}
/* Return buffer length that is required to store the current profile
@@ -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;
@@ -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);
}
@@ -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;
}
diff --git a/compiler-rt/lib/profile/InstrProfilingPort.h b/compiler-rt/lib/profile/InstrProfilingPort.h
index da5b5c0f8bb7..9462cf1a240f 100644
--- a/compiler-rt/lib/profile/InstrProfilingPort.h
+++ b/compiler-rt/lib/profile/InstrProfilingPort.h
@@ -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__);
diff --git a/compiler-rt/lib/profile/InstrProfilingRuntime.cpp b/compiler-rt/lib/profile/InstrProfilingRuntime.cpp
index 679186ef8309..5dff09d70632 100644
--- a/compiler-rt/lib/profile/InstrProfilingRuntime.cpp
+++ b/compiler-rt/lib/profile/InstrProfilingRuntime.cpp
@@ -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();
}
};
diff --git a/compiler-rt/lib/profile/InstrProfilingWriter.c b/compiler-rt/lib/profile/InstrProfilingWriter.c
index d910cbb8f2fc..ae9e1fa6ac13 100644
--- a/compiler-rt/lib/profile/InstrProfilingWriter.c
+++ b/compiler-rt/lib/profile/InstrProfilingWriter.c
@@ -14,6 +14,7 @@
#include "InstrProfiling.h"
#include "InstrProfilingInternal.h"
+#include "InstrProfilingPort.h"
#define INSTR_PROF_VALUE_PROF_DATA
#include "InstrProfData.inc"
@@ -257,10 +258,11 @@ 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;
@@ -268,6 +270,14 @@ lprofWriteDataImpl(ProfDataWriter *Writer, const __llvm_profile_data *DataBegin,
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"
@@ -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);
}
diff --git a/compiler-rt/test/profile/ContinuousSyncMode/basic.c b/compiler-rt/test/profile/ContinuousSyncMode/basic.c
new file mode 100644
index 000000000000..9e29a0b0477e
--- /dev/null
+++ b/compiler-rt/test/profile/ContinuousSyncMode/basic.c
@@ -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;
+}
diff --git a/compiler-rt/test/profile/ContinuousSyncMode/darwin-proof-of-concept.c b/compiler-rt/test/profile/ContinuousSyncMode/darwin-proof-of-concept.c
new file mode 100644
index 000000000000..85caca9a56b4
--- /dev/null
+++ b/compiler-rt/test/profile/ContinuousSyncMode/darwin-proof-of-concept.c
@@ -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;
+}
diff --git a/compiler-rt/test/profile/ContinuousSyncMode/lit.local.cfg.py b/compiler-rt/test/profile/ContinuousSyncMode/lit.local.cfg.py
new file mode 100644
index 000000000000..0918f09cdaa8
--- /dev/null
+++ b/compiler-rt/test/profile/ContinuousSyncMode/lit.local.cfg.py
@@ -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
diff --git a/compiler-rt/test/profile/ContinuousSyncMode/multiple-DSOs.c b/compiler-rt/test/profile/ContinuousSyncMode/multiple-DSOs.c
new file mode 100644
index 000000000000..a54c9af6828d
--- /dev/null
+++ b/compiler-rt/test/profile/ContinuousSyncMode/multiple-DSOs.c
@@ -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;
+}
diff --git a/compiler-rt/test/profile/ContinuousSyncMode/pid-substitution.c b/compiler-rt/test/profile/ContinuousSyncMode/pid-substitution.c
new file mode 100644
index 000000000000..64ba0fbdb7a5
--- /dev/null
+++ b/compiler-rt/test/profile/ContinuousSyncMode/pid-substitution.c
@@ -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;
+}
diff --git a/compiler-rt/test/profile/ContinuousSyncMode/set-file-object.c b/compiler-rt/test/profile/ContinuousSyncMode/set-file-object.c
new file mode 100644
index 000000000000..ac3be3b5237c
--- /dev/null
+++ b/compiler-rt/test/profile/ContinuousSyncMode/set-file-object.c
@@ -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.
+}
diff --git a/compiler-rt/test/profile/ContinuousSyncMode/set-filename.c b/compiler-rt/test/profile/ContinuousSyncMode/set-filename.c
new file mode 100644
index 000000000000..2e6a78950a00
--- /dev/null
+++ b/compiler-rt/test/profile/ContinuousSyncMode/set-filename.c
@@ -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".
+}
diff --git a/llvm/include/llvm/ProfileData/InstrProfData.inc b/llvm/include/llvm/ProfileData/InstrProfData.inc
index 749781b9ac2d..99f41d8fef07 100644
--- a/llvm/include/llvm/ProfileData/InstrProfData.inc
+++ b/llvm/include/llvm/ProfileData/InstrProfData.inc
@@ -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)
@@ -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). */
@@ -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
diff --git a/llvm/lib/ProfileData/InstrProfReader.cpp b/llvm/lib/ProfileData/InstrProfReader.cpp
index 23d078a3ddee..b904f983dceb 100644
--- a/llvm/lib/ProfileData/InstrProfReader.cpp
+++ b/llvm/lib/ProfileData/InstrProfReader.cpp
@@ -362,7 +362,9 @@ 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);
@@ -370,8 +372,10 @@ Error RawInstrProfReader<IntPtrT>::readHeader(
auto PaddingSize = getNumPaddingBytes(NamesSize);
ptr
diff _t DataOffset = sizeof(RawInstrProf::Header);
- ptr
diff _t CountersOffset = DataOffset + DataSizeInBytes;
- ptr
diff _t NamesOffset = CountersOffset + sizeof(uint64_t) * CountersSize;
+ ptr
diff _t CountersOffset =
+ DataOffset + DataSizeInBytes + PaddingBytesBeforeCounters;
+ ptr
diff _t NamesOffset = CountersOffset + (sizeof(uint64_t) * CountersSize) +
+ PaddingBytesAfterCounters;
ptr
diff _t ValueDataOffset = NamesOffset + NamesSize + PaddingSize;
auto *Start = reinterpret_cast<const char *>(&Header);
diff --git a/llvm/test/tools/llvm-profdata/Inputs/c-general.profraw b/llvm/test/tools/llvm-profdata/Inputs/c-general.profraw
index 332d11b140f5..c453d806770d 100644
Binary files a/llvm/test/tools/llvm-profdata/Inputs/c-general.profraw and b/llvm/test/tools/llvm-profdata/Inputs/c-general.profraw
diff er
diff --git a/llvm/test/tools/llvm-profdata/c-general.test b/llvm/test/tools/llvm-profdata/c-general.test
index ed4e61962e42..d4ed384ad603 100644
--- a/llvm/test/tools/llvm-profdata/c-general.test
+++ b/llvm/test/tools/llvm-profdata/c-general.test
@@ -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
@@ -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
diff --git a/llvm/test/tools/llvm-profdata/malformed-ptr-to-counter-array.test b/llvm/test/tools/llvm-profdata/malformed-ptr-to-counter-array.test
index c355bdcbdeae..bad4eb301829 100644
--- a/llvm/test/tools/llvm-profdata/malformed-ptr-to-counter-array.test
+++ b/llvm/test/tools/llvm-profdata/malformed-ptr-to-counter-array.test
@@ -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
diff --git a/llvm/test/tools/llvm-profdata/raw-32-bits-be.test b/llvm/test/tools/llvm-profdata/raw-32-bits-be.test
index d6e1daa1655c..cebd25a07f4a 100644
--- a/llvm/test/tools/llvm-profdata/raw-32-bits-be.test
+++ b/llvm/test/tools/llvm-profdata/raw-32-bits-be.test
@@ -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
diff --git a/llvm/test/tools/llvm-profdata/raw-32-bits-le.test b/llvm/test/tools/llvm-profdata/raw-32-bits-le.test
index cd36aafc9ad9..a176f80ec95a 100644
--- a/llvm/test/tools/llvm-profdata/raw-32-bits-le.test
+++ b/llvm/test/tools/llvm-profdata/raw-32-bits-le.test
@@ -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
diff --git a/llvm/test/tools/llvm-profdata/raw-64-bits-be.test b/llvm/test/tools/llvm-profdata/raw-64-bits-be.test
index 75cc84d68862..c679207102e4 100644
--- a/llvm/test/tools/llvm-profdata/raw-64-bits-be.test
+++ b/llvm/test/tools/llvm-profdata/raw-64-bits-be.test
@@ -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
diff --git a/llvm/test/tools/llvm-profdata/raw-64-bits-le.test b/llvm/test/tools/llvm-profdata/raw-64-bits-le.test
index d8a9c9a7586c..8e179a871625 100644
--- a/llvm/test/tools/llvm-profdata/raw-64-bits-le.test
+++ b/llvm/test/tools/llvm-profdata/raw-64-bits-le.test
@@ -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
diff --git a/llvm/test/tools/llvm-profdata/raw-two-profiles.test b/llvm/test/tools/llvm-profdata/raw-two-profiles.test
index a377375c1768..9cd8a8c7b00e 100644
--- a/llvm/test/tools/llvm-profdata/raw-two-profiles.test
+++ b/llvm/test/tools/llvm-profdata/raw-two-profiles.test
@@ -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
@@ -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
More information about the llvm-commits
mailing list