Index: clang/docs/SourceBasedCodeCoverage.rst =================================================================== --- clang/docs/SourceBasedCodeCoverage.rst +++ clang/docs/SourceBasedCodeCoverage.rst @@ -87,6 +87,16 @@ 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. Index: clang/lib/Driver/ToolChains/Darwin.cpp =================================================================== --- clang/lib/Driver/ToolChains/Darwin.cpp +++ 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,15 @@ CmdArgs.push_back(Symbol); } +/// Add a sectalign directive for \p Segment and \p Section to the maximum +/// expected page size for Darwin (currently 16K). +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 +1127,13 @@ 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 +1143,19 @@ } 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(). + 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, Index: clang/test/Driver/darwin-ld.c =================================================================== --- clang/test/Driver/darwin-ld.c +++ 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 Index: compiler-rt/lib/profile/InstrProfData.inc =================================================================== --- compiler-rt/lib/profile/InstrProfData.inc +++ compiler-rt/lib/profile/InstrProfData.inc @@ -130,7 +130,9 @@ 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 @@ (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). */ Index: compiler-rt/lib/profile/InstrProfiling.h =================================================================== --- compiler-rt/lib/profile/InstrProfiling.h +++ compiler-rt/lib/profile/InstrProfiling.h @@ -38,6 +38,22 @@ #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 @@ * 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 @@ * 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_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 Index: compiler-rt/lib/profile/InstrProfilingBuffer.c =================================================================== --- compiler-rt/lib/profile/InstrProfilingBuffer.c +++ 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 @@ 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 @@ 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 Index: compiler-rt/lib/profile/InstrProfilingFile.c =================================================================== --- compiler-rt/lib/profile/InstrProfilingFile.c +++ 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,8 @@ COMPILER_RT_VISIBILITY void __llvm_profile_set_file_object(FILE *File, int EnableMerge) { + if (__llvm_profile_is_continuous_mode_enabled()) + return; setProfileFile(File); setProfileMergeRequested(EnableMerge); } @@ -347,6 +350,15 @@ 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 +368,99 @@ 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 +524,33 @@ 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 +573,7 @@ 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 +602,7 @@ } truncateCurrentFile(); + initializeProfileForContinuousMode(); } /* Return buffer length that is required to store the current profile @@ -511,7 +639,8 @@ return 0; if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts || - lprofCurFilename.MergePoolSize)) { + lprofCurFilename.MergePoolSize || + __llvm_profile_is_continuous_mode_enabled())) { if (!ForceUseBuf) return lprofCurFilename.FilenamePat; @@ -646,6 +775,8 @@ */ 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 +791,7 @@ 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; } Index: compiler-rt/lib/profile/InstrProfilingPort.h =================================================================== --- compiler-rt/lib/profile/InstrProfilingPort.h +++ 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 +#endif /* defined(_WIN32) */ + #define PROF_ERR(Format, ...) \ fprintf(stderr, "LLVM Profile Error: " Format, __VA_ARGS__); Index: compiler-rt/lib/profile/InstrProfilingRuntime.cpp =================================================================== --- compiler-rt/lib/profile/InstrProfilingRuntime.cpp +++ compiler-rt/lib/profile/InstrProfilingRuntime.cpp @@ -19,8 +19,9 @@ 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(); } }; Index: compiler-rt/lib/profile/InstrProfilingWriter.c =================================================================== --- compiler-rt/lib/profile/InstrProfilingWriter.c +++ 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 @@ 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 @@ 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 @@ 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); } Index: compiler-rt/test/profile/ContinuousSyncMode/basic.c =================================================================== --- /dev/null +++ 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; +} Index: compiler-rt/test/profile/ContinuousSyncMode/darwin-proof-of-concept.c =================================================================== --- /dev/null +++ compiler-rt/test/profile/ContinuousSyncMode/darwin-proof-of-concept.c @@ -0,0 +1,146 @@ +// 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 +#include +#include +#include +#include +#include + +__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; + } + + 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; + } + + 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; + } + } + + 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; + } + } + + 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; +} Index: compiler-rt/test/profile/ContinuousSyncMode/lit.local.cfg.py =================================================================== --- /dev/null +++ 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 Index: compiler-rt/test/profile/ContinuousSyncMode/multiple-DSOs.c =================================================================== --- /dev/null +++ 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; +} Index: compiler-rt/test/profile/ContinuousSyncMode/pid-substitution.c =================================================================== --- /dev/null +++ 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 +#include + +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; +} Index: compiler-rt/test/profile/ContinuousSyncMode/set-file-object.c =================================================================== --- /dev/null +++ compiler-rt/test/profile/ContinuousSyncMode/set-file-object.c @@ -0,0 +1,31 @@ +// 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: Profile data not written to file: already written. + +#include + +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. +} Index: compiler-rt/test/profile/ContinuousSyncMode/set-filename.c =================================================================== --- /dev/null +++ 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 + +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". +} Index: llvm/include/llvm/ProfileData/InstrProfData.inc =================================================================== --- llvm/include/llvm/ProfileData/InstrProfData.inc +++ llvm/include/llvm/ProfileData/InstrProfData.inc @@ -130,7 +130,9 @@ 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 @@ (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 @@ #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 Index: llvm/lib/ProfileData/InstrProfReader.cpp =================================================================== --- llvm/lib/ProfileData/InstrProfReader.cpp +++ llvm/lib/ProfileData/InstrProfReader.cpp @@ -362,7 +362,9 @@ 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 @@ auto PaddingSize = getNumPaddingBytes(NamesSize); ptrdiff_t DataOffset = sizeof(RawInstrProf::Header); - ptrdiff_t CountersOffset = DataOffset + DataSizeInBytes; - ptrdiff_t NamesOffset = CountersOffset + sizeof(uint64_t) * CountersSize; + ptrdiff_t CountersOffset = + DataOffset + DataSizeInBytes + PaddingBytesBeforeCounters; + ptrdiff_t NamesOffset = CountersOffset + (sizeof(uint64_t) * CountersSize) + + PaddingBytesAfterCounters; ptrdiff_t ValueDataOffset = NamesOffset + NamesSize + PaddingSize; auto *Start = reinterpret_cast(&Header); Index: llvm/test/tools/llvm-profdata/c-general.test =================================================================== --- llvm/test/tools/llvm-profdata/c-general.test +++ llvm/test/tools/llvm-profdata/c-general.test @@ -5,7 +5,7 @@ $ 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 @@ 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 Index: llvm/test/tools/llvm-profdata/raw-32-bits-be.test =================================================================== --- llvm/test/tools/llvm-profdata/raw-32-bits-be.test +++ 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 Index: llvm/test/tools/llvm-profdata/raw-32-bits-le.test =================================================================== --- llvm/test/tools/llvm-profdata/raw-32-bits-le.test +++ 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 Index: llvm/test/tools/llvm-profdata/raw-64-bits-be.test =================================================================== --- llvm/test/tools/llvm-profdata/raw-64-bits-be.test +++ 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 Index: llvm/test/tools/llvm-profdata/raw-64-bits-le.test =================================================================== --- llvm/test/tools/llvm-profdata/raw-64-bits-le.test +++ 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 Index: llvm/test/tools/llvm-profdata/raw-two-profiles.test =================================================================== --- llvm/test/tools/llvm-profdata/raw-two-profiles.test +++ 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 '\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