[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