[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
This commit is contained in:
Vedant Kumar 2019-09-19 11:56:43 -07:00
parent ade776b584
commit d889d1efef
27 changed files with 690 additions and 25 deletions

View File

@ -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.

View File

@ -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,

View File

@ -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

View File

@ -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). */

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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__);

View File

@ -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();
}
};

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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.
}

View File

@ -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".
}

View File

@ -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

View File

@ -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);
ptrdiff_t DataOffset = sizeof(RawInstrProf::Header);
ptrdiff_t CountersOffset = DataOffset + DataSizeInBytes;
ptrdiff_t NamesOffset = CountersOffset + sizeof(uint64_t) * CountersSize;
ptrdiff_t CountersOffset =
DataOffset + DataSizeInBytes + PaddingBytesBeforeCounters;
ptrdiff_t NamesOffset = CountersOffset + (sizeof(uint64_t) * CountersSize) +
PaddingBytesAfterCounters;
ptrdiff_t ValueDataOffset = NamesOffset + NamesSize + PaddingSize;
auto *Start = reinterpret_cast<const char *>(&Header);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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