forked from OSchip/llvm-project
[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:
parent
ade776b584
commit
d889d1efef
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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). */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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.
|
||||
}
|
|
@ -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".
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Binary file not shown.
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue