diff --git a/clang/docs/SourceBasedCodeCoverage.rst b/clang/docs/SourceBasedCodeCoverage.rst index 27a1950a16028..0beb284e475ee 100644 --- a/clang/docs/SourceBasedCodeCoverage.rst +++ b/clang/docs/SourceBasedCodeCoverage.rst @@ -87,6 +87,16 @@ directory structure will be created. Additionally, the following special be between 1 and 9. The merge pool specifier can only occur once per filename pattern. +* "%c" expands out to nothing, but enables a mode in which profile counter + updates are continuously synced to a file. This means that if the + instrumented program crashes, or is killed by a signal, perfect coverage + information can still be recovered. Continuous mode is not yet compatible with + the "%Nm" merging mode described above, does not support value profiling for + PGO, and is only supported on Darwin. Support for Linux may be mostly + complete but requires testing, and support for Fuchsia/Windows may require + more extensive changes: please get involved if you are interested in porting + this feature. + .. code-block:: console # Step 2: Run the program. diff --git a/clang/lib/Driver/CMakeLists.txt b/clang/lib/Driver/CMakeLists.txt index d90c0ff436071..36e7184cebd73 100644 --- a/clang/lib/Driver/CMakeLists.txt +++ b/clang/lib/Driver/CMakeLists.txt @@ -1,6 +1,7 @@ set(LLVM_LINK_COMPONENTS BinaryFormat Option + ProfileData Support ) diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index 0c4874107f4a5..31fb95c29c191 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -19,6 +19,7 @@ #include "clang/Driver/SanitizerArgs.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Option/ArgList.h" +#include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/TargetParser.h" @@ -1114,6 +1115,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; @@ -1121,21 +1135,40 @@ 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"); } else { addExportedSymbol(CmdArgs, "___llvm_profile_filename"); addExportedSymbol(CmdArgs, "___llvm_profile_raw_version"); - addExportedSymbol(CmdArgs, "_lprofCurFilename"); } addExportedSymbol(CmdArgs, "_lprofDirMode"); } + + // Align __llvm_prf_{cnts,data} sections to the maximum expected page + // alignment. This allows profile counters to be mmap()'d to disk. Note that + // it's not enough to just page-align __llvm_prf_cnts: the following section + // must also be page-aligned so that its data is not clobbered by mmap(). + // + // The section alignment is only needed when continuous profile sync is + // enabled, but this is expected to be the default in Xcode. Specifying the + // extra alignment also allows the same binary to be used with/without sync + // enabled. + if (!ForGCOV) { + for (auto IPSK : {llvm::IPSK_cnts, llvm::IPSK_data}) { + addSectalignToPage( + Args, CmdArgs, "__DATA", + llvm::getInstrProfSectionName(IPSK, llvm::Triple::MachO, + /*AddSegmentInfo=*/false)); + } + } } void DarwinClang::AddLinkSanitizerLibArgs(const ArgList &Args, diff --git a/clang/test/Driver/darwin-ld.c b/clang/test/Driver/darwin-ld.c index f01eeb4ea28e0..a9f2988ec2f39 100644 --- a/clang/test/Driver/darwin-ld.c +++ b/clang/test/Driver/darwin-ld.c @@ -345,6 +345,12 @@ // RUN: FileCheck -check-prefix=LINK_PROFILE_FIRST %s < %t.log // LINK_PROFILE_FIRST: {{ld(.exe)?"}} "{{[^"]+}}libclang_rt.profile_{{[a-z]+}}.a" +// RUN: %clang -target x86_64-apple-darwin12 -fprofile-instr-generate -### %t.o 2> %t.log +// RUN: FileCheck -check-prefix=PROFILE_SECTALIGN %s < %t.log +// RUN: %clang -target arm64-apple-ios12 -fprofile-instr-generate -### %t.o 2> %t.log +// RUN: FileCheck -check-prefix=PROFILE_SECTALIGN %s < %t.log +// PROFILE_SECTALIGN: "-sectalign" "__DATA" "__llvm_prf_cnts" "0x4000" "-sectalign" "__DATA" "__llvm_prf_data" "0x4000" + // RUN: %clang -target x86_64-apple-darwin12 -fprofile-instr-generate -exported_symbols_list /dev/null -### %t.o 2> %t.log // RUN: FileCheck -check-prefix=PROFILE_EXPORT %s < %t.log // RUN: %clang -target x86_64-apple-darwin12 -fprofile-instr-generate -Wl,-exported_symbols_list,/dev/null -### %t.o 2> %t.log @@ -355,7 +361,7 @@ // RUN: FileCheck -check-prefix=PROFILE_EXPORT %s < %t.log // RUN: %clang -target x86_64-apple-darwin12 -fprofile-instr-generate -Xlinker -exported_symbols_list -Xlinker /dev/null -### %t.o 2> %t.log // RUN: FileCheck -check-prefix=PROFILE_EXPORT %s < %t.log -// PROFILE_EXPORT: "-exported_symbol" "___llvm_profile_filename" "-exported_symbol" "___llvm_profile_raw_version" "-exported_symbol" "_lprofCurFilename" +// PROFILE_EXPORT: "-exported_symbol" "___llvm_profile_filename" "-exported_symbol" "___llvm_profile_raw_version" // // RUN: %clang -target x86_64-apple-darwin12 -fprofile-instr-generate --coverage -### %t.o 2> %t.log // RUN: FileCheck -check-prefix=NO_PROFILE_EXPORT %s < %t.log diff --git a/compiler-rt/lib/profile/InstrProfData.inc b/compiler-rt/lib/profile/InstrProfData.inc index 7078af5f4cf8d..99f41d8fef077 100644 --- a/compiler-rt/lib/profile/InstrProfData.inc +++ b/compiler-rt/lib/profile/InstrProfData.inc @@ -130,7 +130,9 @@ INSTR_PROF_VALUE_NODE(PtrToNodeT, llvm::Type::getInt8PtrTy(Ctx), Next, \ INSTR_PROF_RAW_HEADER(uint64_t, Magic, __llvm_profile_get_magic()) INSTR_PROF_RAW_HEADER(uint64_t, Version, __llvm_profile_get_version()) INSTR_PROF_RAW_HEADER(uint64_t, DataSize, DataSize) +INSTR_PROF_RAW_HEADER(uint64_t, PaddingBytesBeforeCounters, PaddingBytesBeforeCounters) INSTR_PROF_RAW_HEADER(uint64_t, CountersSize, CountersSize) +INSTR_PROF_RAW_HEADER(uint64_t, PaddingBytesAfterCounters, PaddingBytesAfterCounters) INSTR_PROF_RAW_HEADER(uint64_t, NamesSize, NamesSize) INSTR_PROF_RAW_HEADER(uint64_t, CountersDelta, (uintptr_t)CountersBegin) INSTR_PROF_RAW_HEADER(uint64_t, NamesDelta, (uintptr_t)NamesBegin) @@ -628,7 +630,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure, (uint64_t)'f' << 16 | (uint64_t)'R' << 8 | (uint64_t)129 /* Raw profile format version (start from 1). */ -#define INSTR_PROF_RAW_VERSION 4 +#define INSTR_PROF_RAW_VERSION 5 /* Indexed profile format version (start from 1). */ #define INSTR_PROF_INDEX_VERSION 5 /* Coverage mapping format vresion (start from 0). */ diff --git a/compiler-rt/lib/profile/InstrProfiling.h b/compiler-rt/lib/profile/InstrProfiling.h index 91afd5e6b92df..a992de4c83799 100644 --- a/compiler-rt/lib/profile/InstrProfiling.h +++ b/compiler-rt/lib/profile/InstrProfiling.h @@ -10,6 +10,7 @@ #define PROFILE_INSTRPROFILING_H_ #include "InstrProfilingPort.h" +#include #define INSTR_PROF_VISIBILITY COMPILER_RT_VISIBILITY #include "InstrProfData.inc" @@ -37,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. @@ -125,7 +142,7 @@ int __llvm_orderfile_write_file(void); /*! * \brief this is a wrapper interface to \c __llvm_profile_write_file. * After this interface is invoked, a arleady dumped flag will be set - * so that profile won't be dumped again during program exit. + * so that profile won't be dumped again during program exit. * Invocation of interface __llvm_profile_reset_counters will clear * the flag. This interface is designed to be used to collect profile * data from user selected hot regions. The use model is @@ -154,9 +171,47 @@ int __llvm_orderfile_dump(void); * * \c Name is not copied, so it must remain valid. Passing NULL resets the * filename logic to the default behaviour. + * + * 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); +/*! + * \brief Set the FILE object for writing instrumentation data. + * + * Sets the FILE object to be used for subsequent calls to + * \a __llvm_profile_write_file(). The profile file name set by environment + * variable, command-line option, or calls to \a __llvm_profile_set_filename + * will be ignored. + * + * \c File will not be closed after a call to \a __llvm_profile_write_file() but + * it may be flushed. Passing NULL restores default behavior. + * + * If \c EnableMerge is nonzero, the runtime will always merge profiling data + * with the contents of the profiling file. If EnableMerge is zero, the runtime + * may still merge the data if it would have merged for another reason (for + * example, because of a %m specifier in the file 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); + /*! \brief Register to write instrumentation data to file at exit. */ int __llvm_profile_register_write_file_atexit(void); @@ -177,7 +232,12 @@ const char *__llvm_profile_get_path_prefix(); * \brief Return filename (including path) of the profile data. Note that if the * user calls __llvm_profile_set_filename later after invoking this interface, * the actual file name may differ from what is returned here. - * Side-effect: this API call will invoke malloc with dynamic memory allocation. + * Side-effect: this API call will invoke malloc with dynamic memory allocation + * (the returned pointer must be passed to `free` to avoid a leak). + * + * Note: There may be multiple copies of the profile runtime (one for each + * instrumented image/DSO). This API only retrieves the filename from the copy + * of the runtime available to the calling image. */ const char *__llvm_profile_get_filename(); @@ -191,6 +251,24 @@ uint64_t __llvm_profile_get_version(void); uint64_t __llvm_profile_get_data_size(const __llvm_profile_data *Begin, const __llvm_profile_data *End); +/* ! \brief Given the sizes of the data and counter information, return the + * number of padding bytes before and after the counters, and after the names, + * in the raw profile. + * + * Note: In this context, "size" means "number of entries", i.e. the first two + * arguments must be the result of __llvm_profile_get_data_size() and of + * (__llvm_profile_end_counters() - __llvm_profile_begin_counters()) resp. + * + * Note: When mmap() mode is disabled, no padding bytes before/after counters + * are needed. However, in mmap() mode, the counter section in the raw profile + * must be page-aligned: this API computes the number of padding bytes + * needed to achieve that. + */ +void __llvm_profile_get_padding_sizes_for_counters( + uint64_t DataSize, uint64_t CountersSize, uint64_t NamesSize, + uint64_t *PaddingBytesBeforeCounters, uint64_t *PaddingBytesAfterCounters, + uint64_t *PaddingBytesAfterNames); + /*! * \brief Set the flag that profile data has been dumped to the file. * This is useful for users to disable dumping profile data to the file for diff --git a/compiler-rt/lib/profile/InstrProfilingBuffer.c b/compiler-rt/lib/profile/InstrProfilingBuffer.c index 5bdeb8e328078..089ff5a0153d3 100644 --- a/compiler-rt/lib/profile/InstrProfilingBuffer.c +++ b/compiler-rt/lib/profile/InstrProfilingBuffer.c @@ -8,6 +8,27 @@ #include "InstrProfiling.h" #include "InstrProfilingInternal.h" +#include "InstrProfilingPort.h" + +/* When continuous mode is enabled (%c), this parameter is set to 1. This is + * incompatible with the in-process merging mode. Lifting this restriction + * may be complicated, as merging mode requires a lock on the profile, and + * mmap() mode would require that lock to be held for the entire process + * lifetime. + * + * This parameter is defined here in InstrProfilingBuffer.o, instead of in + * InstrProfilingFile.o, to sequester all libc-dependent code in + * InstrProfilingFile.o. The test `instrprof-without-libc` will break if this + * layering is violated. */ +static int ContinuouslySyncProfile = 0; + +COMPILER_RT_VISIBILITY int __llvm_profile_is_continuous_mode_enabled(void) { + return ContinuouslySyncProfile; +} + +COMPILER_RT_VISIBILITY void __llvm_profile_enable_continuous_mode(void) { + ContinuouslySyncProfile = 1; +} COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_size_for_buffer(void) { @@ -30,6 +51,41 @@ uint64_t __llvm_profile_get_data_size(const __llvm_profile_data *Begin, sizeof(__llvm_profile_data); } +/// Calculate the number of padding bytes needed to add to \p Offset in order +/// for (\p Offset + Padding) to be page-aligned. +static uint64_t calculateBytesNeededToPageAlign(uint64_t Offset, + unsigned PageSize) { + uint64_t OffsetModPage = Offset % PageSize; + if (OffsetModPage > 0) + return PageSize - OffsetModPage; + return 0; +} + +COMPILER_RT_VISIBILITY +void __llvm_profile_get_padding_sizes_for_counters( + uint64_t DataSize, uint64_t CountersSize, uint64_t NamesSize, + uint64_t *PaddingBytesBeforeCounters, uint64_t *PaddingBytesAfterCounters, + uint64_t *PaddingBytesAfterNames) { + if (!__llvm_profile_is_continuous_mode_enabled()) { + *PaddingBytesBeforeCounters = 0; + *PaddingBytesAfterCounters = 0; + *PaddingBytesAfterNames = __llvm_profile_get_num_padding_bytes(NamesSize); + return; + } + + // In continuous mode, the file offsets for headers and for the start of + // counter sections need to be page-aligned. + unsigned PageSize = getpagesize(); + uint64_t DataSizeInBytes = DataSize * sizeof(__llvm_profile_data); + uint64_t CountersSizeInBytes = CountersSize * sizeof(uint64_t); + *PaddingBytesBeforeCounters = calculateBytesNeededToPageAlign( + sizeof(__llvm_profile_header) + DataSizeInBytes, PageSize); + *PaddingBytesAfterCounters = + calculateBytesNeededToPageAlign(CountersSizeInBytes, PageSize); + *PaddingBytesAfterNames = + calculateBytesNeededToPageAlign(NamesSize, PageSize); +} + COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_size_for_buffer_internal( const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd, @@ -37,11 +93,21 @@ uint64_t __llvm_profile_get_size_for_buffer_internal( const char *NamesBegin, const char *NamesEnd) { /* Match logic in __llvm_profile_write_buffer(). */ const uint64_t NamesSize = (NamesEnd - NamesBegin) * sizeof(char); - const uint8_t Padding = __llvm_profile_get_num_padding_bytes(NamesSize); + uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd); + uint64_t CountersSize = CountersEnd - CountersBegin; + + /* Determine how much padding is needed before/after the counters and after + * the names. */ + uint64_t PaddingBytesBeforeCounters, PaddingBytesAfterCounters, + PaddingBytesAfterNames; + __llvm_profile_get_padding_sizes_for_counters( + DataSize, CountersSize, NamesSize, &PaddingBytesBeforeCounters, + &PaddingBytesAfterCounters, &PaddingBytesAfterNames); + return sizeof(__llvm_profile_header) + - (__llvm_profile_get_data_size(DataBegin, DataEnd) * - sizeof(__llvm_profile_data)) + - (CountersEnd - CountersBegin) * sizeof(uint64_t) + NamesSize + Padding; + (DataSize * sizeof(__llvm_profile_data)) + PaddingBytesBeforeCounters + + (CountersSize * sizeof(uint64_t)) + PaddingBytesAfterCounters + + NamesSize + PaddingBytesAfterNames; } COMPILER_RT_VISIBILITY diff --git a/compiler-rt/lib/profile/InstrProfilingFile.c b/compiler-rt/lib/profile/InstrProfilingFile.c index 4e6e34921cc19..be4b97cffeb65 100644 --- a/compiler-rt/lib/profile/InstrProfilingFile.c +++ b/compiler-rt/lib/profile/InstrProfilingFile.c @@ -33,12 +33,13 @@ #include "InstrProfiling.h" #include "InstrProfilingInternal.h" +#include "InstrProfilingPort.h" #include "InstrProfilingUtil.h" /* From where is profile name specified. * The order the enumerators define their * precedence. Re-order them may lead to - * runtime behavior change. */ + * runtime behavior change. */ typedef enum ProfileNameSpecifier { PNS_unknown = 0, PNS_default, @@ -72,7 +73,6 @@ typedef struct lprofFilename { * by runtime. */ unsigned OwnsFilenamePat; const char *ProfilePathPrefix; - const char *Filename; char PidChars[MAX_PID_SIZE]; char Hostname[COMPILER_RT_MAX_HOSTLEN]; unsigned NumPids; @@ -90,12 +90,36 @@ typedef struct lprofFilename { ProfileNameSpecifier PNS; } lprofFilename; -COMPILER_RT_WEAK lprofFilename lprofCurFilename = {0, 0, 0, 0, {0}, - {0}, 0, 0, 0, {0}, 0, PNS_unknown}; +static lprofFilename lprofCurFilename = {0, 0, 0, {0}, {0}, 0, + 0, 0, {0}, 0, PNS_unknown}; + +static int ProfileMergeRequested = 0; +static int isProfileMergeRequested() { return ProfileMergeRequested; } +static void setProfileMergeRequested(int EnableMerge) { + ProfileMergeRequested = EnableMerge; +} + +static FILE *ProfileFile = NULL; +static FILE *getProfileFile() { return ProfileFile; } +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); +} static int getCurFilenameLength(); static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf); -static unsigned doMerging() { return lprofCurFilename.MergePoolSize; } +static unsigned doMerging() { + return lprofCurFilename.MergePoolSize || isProfileMergeRequested(); +} /* Return 1 if there is an error, otherwise return 0. */ static uint32_t fileWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs, @@ -229,11 +253,16 @@ static void createProfileDir(const char *Filename) { * its instrumented shared libraries dump profile data into their own data file. */ static FILE *openFileForMerging(const char *ProfileFileName, int *MergeDone) { - FILE *ProfileFile; + FILE *ProfileFile = NULL; int rc; - createProfileDir(ProfileFileName); - ProfileFile = lprofOpenFileEx(ProfileFileName); + ProfileFile = getProfileFile(); + if (ProfileFile) { + lprofLockFileHandle(ProfileFile); + } else { + createProfileDir(ProfileFileName); + ProfileFile = lprofOpenFileEx(ProfileFileName); + } if (!ProfileFile) return NULL; @@ -248,6 +277,16 @@ static FILE *openFileForMerging(const char *ProfileFileName, int *MergeDone) { return ProfileFile; } +static FILE *getFileObject(const char *OutputName) { + FILE *File; + File = getProfileFile(); + if (File != NULL) { + return File; + } + + return fopen(OutputName, "ab"); +} + /* Write profile data to file \c OutputName. */ static int writeFile(const char *OutputName) { int RetVal; @@ -255,10 +294,10 @@ static int writeFile(const char *OutputName) { int MergeDone = 0; VPMergeHook = &lprofMergeValueProfData; - if (!doMerging()) - OutputFile = fopen(OutputName, "ab"); - else + if (doMerging()) OutputFile = openFileForMerging(OutputName, &MergeDone); + else + OutputFile = getFileObject(OutputName); if (!OutputFile) return -1; @@ -269,7 +308,15 @@ static int writeFile(const char *OutputName) { initFileWriter(&fileWriter, OutputFile); RetVal = lprofWriteData(&fileWriter, lprofGetVPDataReader(), MergeDone); - fclose(OutputFile); + if (OutputFile == getProfileFile()) { + fflush(OutputFile); + if (doMerging()) { + lprofUnlockFileHandle(OutputFile); + } + } else { + fclose(OutputFile); + } + return RetVal; } @@ -294,6 +341,8 @@ static int writeOrderFile(const char *OutputName) { return RetVal; } +#define LPROF_INIT_ONCE_ENV "__LLVM_PROFILE_RT_INIT_ONCE" + static void truncateCurrentFile(void) { const char *Filename; char *FilenameBuf; @@ -311,6 +360,18 @@ 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. */ + int initialized = getenv(LPROF_INIT_ONCE_ENV) != NULL; + if (initialized) + return; +#if defined(_WIN32) + _putenv(LPROF_INIT_ONCE_ENV "=" LPROF_INIT_ONCE_ENV); +#else + setenv(LPROF_INIT_ONCE_ENV, LPROF_INIT_ONCE_ENV, 1); +#endif + createProfileDir(Filename); /* Truncate the file. Later we'll reopen and append. */ @@ -326,6 +387,13 @@ static void exitSignalHandler(int sig) { } static void installExitSignalHandlers(void) { + if (__llvm_profile_is_continuous_mode_enabled()) { + PROF_WARN( + "%s", + "Not installing exit handlers because continuous mode is enabled."); + return; + } + unsigned I; struct sigaction sigact; int err; @@ -340,6 +408,99 @@ static void installExitSignalHandlers(void) { } } +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 = %" PRIu64 ", pagesz = %u.\n", + (uint64_t) 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: %" PRIu64 "\n" + " - Fileno: %d\n" + " - FileOffsetToCounters: %" PRIu64 "\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) { @@ -382,8 +543,6 @@ static int parseFilenamePattern(const char *FilenamePat, /* Clean up cached prefix and filename. */ if (lprofCurFilename.ProfilePathPrefix) free((void *)lprofCurFilename.ProfilePathPrefix); - if (lprofCurFilename.Filename) - free((void *)lprofCurFilename.Filename); if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) { free((void *)lprofCurFilename.FilenamePat); @@ -417,12 +576,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; @@ -461,6 +641,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; @@ -489,6 +670,7 @@ static void parseAndSetFilename(const char *FilenamePat, } truncateCurrentFile(); + initializeProfileForContinuousMode(); installExitSignalHandlers(); } @@ -532,7 +714,8 @@ static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf) { return 0; if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts || - lprofCurFilename.MergePoolSize || lprofCurFilename.NumExitSignals)) { + lprofCurFilename.MergePoolSize || lprofCurFilename.NumExitSignals || + __llvm_profile_is_continuous_mode_enabled())) { if (!ForceUseBuf) return lprofCurFilename.FilenamePat; @@ -623,9 +806,6 @@ const char *__llvm_profile_get_filename(void) { char *FilenameBuf; const char *Filename; - if (lprofCurFilename.Filename) - return lprofCurFilename.Filename; - Length = getCurFilenameLength(); FilenameBuf = (char *)malloc(Length + 1); if (!FilenameBuf) { @@ -636,7 +816,6 @@ const char *__llvm_profile_get_filename(void) { if (!Filename) return "\0"; - lprofCurFilename.Filename = FilenameBuf; return FilenameBuf; } @@ -653,7 +832,7 @@ void __llvm_profile_initialize_file(void) { EnvFilenamePat = getFilenamePatFromEnv(); if (EnvFilenamePat) { - /* Pass CopyFilenamePat = 1, to ensure that the filename would be valid + /* Pass CopyFilenamePat = 1, to ensure that the filename would be valid at the moment when __llvm_profile_write_file() gets executed. */ parseAndSetFilename(EnvFilenamePat, PNS_environment, 1); return; @@ -674,6 +853,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); } @@ -688,9 +869,8 @@ int __llvm_profile_write_file(void) { char *FilenameBuf; int PDeathSig = 0; - if (lprofProfileDumped()) { - PROF_NOTE("Profile data not written to file: %s.\n", - "already written"); + if (lprofProfileDumped() || __llvm_profile_is_continuous_mode_enabled()) { + PROF_NOTE("Profile data not written to file: %s.\n", "already written"); return 0; } diff --git a/compiler-rt/lib/profile/InstrProfilingPort.h b/compiler-rt/lib/profile/InstrProfilingPort.h index da5b5c0f8bb7d..9462cf1a240fc 100644 --- a/compiler-rt/lib/profile/InstrProfilingPort.h +++ b/compiler-rt/lib/profile/InstrProfilingPort.h @@ -99,6 +99,16 @@ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ +#if defined(_WIN32) +static inline size_t getpagesize() { + SYSTEM_INFO S; + GetNativeSystemInfo(&S); + return S.dwPageSize; +} +#else /* defined(_WIN32) */ +#include +#endif /* defined(_WIN32) */ + #define PROF_ERR(Format, ...) \ fprintf(stderr, "LLVM Profile Error: " Format, __VA_ARGS__); diff --git a/compiler-rt/lib/profile/InstrProfilingRuntime.cc b/compiler-rt/lib/profile/InstrProfilingRuntime.cc index 679186ef83094..5dff09d706329 100644 --- a/compiler-rt/lib/profile/InstrProfilingRuntime.cc +++ b/compiler-rt/lib/profile/InstrProfilingRuntime.cc @@ -19,8 +19,9 @@ namespace { class RegisterRuntime { public: RegisterRuntime() { - __llvm_profile_register_write_file_atexit(); __llvm_profile_initialize_file(); + if (!__llvm_profile_is_continuous_mode_enabled()) + __llvm_profile_register_write_file_atexit(); } }; diff --git a/compiler-rt/lib/profile/InstrProfilingUtil.c b/compiler-rt/lib/profile/InstrProfilingUtil.c index 5e479ae7150ac..02d100792db86 100644 --- a/compiler-rt/lib/profile/InstrProfilingUtil.c +++ b/compiler-rt/lib/profile/InstrProfilingUtil.c @@ -154,6 +154,26 @@ COMPILER_RT_VISIBILITY int lprofUnlockFd(int fd) { #endif } +COMPILER_RT_VISIBILITY int lprofLockFileHandle(FILE *F) { + int fd; +#if defined(_WIN32) + fd = _fileno(F); +#else + fd = fileno(F); +#endif + return lprofLockFd(fd); +} + +COMPILER_RT_VISIBILITY int lprofUnlockFileHandle(FILE *F) { + int fd; +#if defined(_WIN32) + fd = _fileno(F); +#else + fd = fileno(F); +#endif + return lprofUnlockFd(fd); +} + COMPILER_RT_VISIBILITY FILE *lprofOpenFileEx(const char *ProfileName) { FILE *f; int fd; diff --git a/compiler-rt/lib/profile/InstrProfilingUtil.h b/compiler-rt/lib/profile/InstrProfilingUtil.h index 9cd0860fda237..efba94ca76396 100644 --- a/compiler-rt/lib/profile/InstrProfilingUtil.h +++ b/compiler-rt/lib/profile/InstrProfilingUtil.h @@ -23,6 +23,8 @@ unsigned __llvm_profile_get_dir_mode(void); int lprofLockFd(int fd); int lprofUnlockFd(int fd); +int lprofLockFileHandle(FILE *F); +int lprofUnlockFileHandle(FILE *F); /*! Open file \c Filename for read+write with write * lock for exclusive access. The caller will block diff --git a/compiler-rt/lib/profile/InstrProfilingWriter.c b/compiler-rt/lib/profile/InstrProfilingWriter.c index d910cbb8f2fc0..0b2e17612ab3e 100644 --- a/compiler-rt/lib/profile/InstrProfilingWriter.c +++ b/compiler-rt/lib/profile/InstrProfilingWriter.c @@ -14,6 +14,7 @@ #include "InstrProfiling.h" #include "InstrProfilingInternal.h" +#include "InstrProfilingPort.h" #define INSTR_PROF_VALUE_PROF_DATA #include "InstrProfData.inc" @@ -257,10 +258,11 @@ lprofWriteDataImpl(ProfDataWriter *Writer, const __llvm_profile_data *DataBegin, const uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd); const uint64_t CountersSize = CountersEnd - CountersBegin; const uint64_t NamesSize = NamesEnd - NamesBegin; - const uint64_t Padding = __llvm_profile_get_num_padding_bytes(NamesSize); /* Enough zeroes for padding. */ - const char Zeroes[sizeof(uint64_t)] = {0}; + unsigned PageSize = getpagesize(); + char *Zeroes = (char *)COMPILER_RT_ALLOCA(PageSize); + memset(Zeroes, 0, PageSize); /* Create the header. */ __llvm_profile_header Header; @@ -268,6 +270,14 @@ lprofWriteDataImpl(ProfDataWriter *Writer, const __llvm_profile_data *DataBegin, if (!DataSize) return 0; + /* Determine how much padding is needed before/after the counters and after + * the names. */ + uint64_t PaddingBytesBeforeCounters, PaddingBytesAfterCounters, + PaddingBytesAfterNames; + __llvm_profile_get_padding_sizes_for_counters( + DataSize, CountersSize, NamesSize, &PaddingBytesBeforeCounters, + &PaddingBytesAfterCounters, &PaddingBytesAfterNames); + /* Initialize header structure. */ #define INSTR_PROF_RAW_HEADER(Type, Name, Init) Header.Name = Init; #include "InstrProfData.inc" @@ -276,11 +286,17 @@ lprofWriteDataImpl(ProfDataWriter *Writer, const __llvm_profile_data *DataBegin, ProfDataIOVec IOVec[] = { {&Header, sizeof(__llvm_profile_header), 1}, {DataBegin, sizeof(__llvm_profile_data), DataSize}, + {Zeroes, sizeof(uint8_t), PaddingBytesBeforeCounters}, {CountersBegin, sizeof(uint64_t), CountersSize}, + {Zeroes, sizeof(uint8_t), PaddingBytesAfterCounters}, {SkipNameDataWrite ? NULL : NamesBegin, sizeof(uint8_t), NamesSize}, - {Zeroes, sizeof(uint8_t), Padding}}; + {Zeroes, sizeof(uint8_t), PaddingBytesAfterNames}}; if (Writer->Write(Writer, IOVec, sizeof(IOVec) / sizeof(*IOVec))) return -1; + /* Value profiling is not yet supported in continuous mode. */ + if (__llvm_profile_is_continuous_mode_enabled()) + return 0; + return writeValueProfData(Writer, VPDataReader, DataBegin, DataEnd); } diff --git a/compiler-rt/test/profile/ContinuousSyncMode/basic.c b/compiler-rt/test/profile/ContinuousSyncMode/basic.c new file mode 100644 index 0000000000000..9e29a0b0477ec --- /dev/null +++ b/compiler-rt/test/profile/ContinuousSyncMode/basic.c @@ -0,0 +1,32 @@ +// RUN: %clang -fprofile-instr-generate -fcoverage-mapping -o %t.exe %s +// RUN: echo "garbage" > %t.profraw +// RUN: env LLVM_PROFILE_FILE="%c%t.profraw" %run %t.exe +// RUN: llvm-profdata show --counts --all-functions %t.profraw | FileCheck %s -check-prefix=CHECK-COUNTS +// RUN: llvm-profdata merge -o %t.profdata %t.profraw +// RUN: llvm-cov report %t.exe -instr-profile %t.profdata | FileCheck %s -check-prefix=CHECK-COVERAGE + +// CHECK-COUNTS: Counters: +// CHECK-COUNTS-NEXT: main: +// CHECK-COUNTS-NEXT: Hash: 0x{{.*}} +// CHECK-COUNTS-NEXT: Counters: 2 +// CHECK-COUNTS-NEXT: Function count: 1 +// CHECK-COUNTS-NEXT: Block counts: [1] +// CHECK-COUNTS-NEXT: Instrumentation level: Front-end +// CHECK-COUNTS-NEXT: Functions shown: 1 +// CHECK-COUNTS-NEXT: Total functions: 1 +// CHECK-COUNTS-NEXT: Maximum function count: 1 +// CHECK-COUNTS-NEXT: Maximum internal block count: 1 + +// CHECK-COVERAGE: Filename Regions Missed Regions Cover Functions Missed Functions Executed Lines Missed Lines Cover +// CHECK-COVERAGE-NEXT: --- +// CHECK-COVERAGE-NEXT: basic.c 4 1 75.00% 1 0 100.00% 5 2 60.00% +// CHECK-COVERAGE-NEXT: --- +// CHECK-COVERAGE-NEXT: TOTAL 4 1 75.00% 1 0 100.00% 5 2 60.00% + +extern int __llvm_profile_is_continuous_mode_enabled(void); + +int main() { + if (__llvm_profile_is_continuous_mode_enabled()) + return 0; + return 1; +} diff --git a/compiler-rt/test/profile/ContinuousSyncMode/darwin-proof-of-concept.c b/compiler-rt/test/profile/ContinuousSyncMode/darwin-proof-of-concept.c new file mode 100644 index 0000000000000..85caca9a56b40 --- /dev/null +++ b/compiler-rt/test/profile/ContinuousSyncMode/darwin-proof-of-concept.c @@ -0,0 +1,151 @@ +// Test whether mmap'ing profile counters onto an open file is feasible. As +// this involves some platform-specific logic, this test is designed to be a +// minimum viable proof-of-concept: it may be useful when porting the mmap() +// mode to a new platform, but is not in and of itself a test of the profiling +// runtime. + +// REQUIRES: darwin + +// Align counters and data to the maximum expected page size (16K). +// RUN: %clang -g -o %t %s \ +// RUN: -Wl,-sectalign,__DATA,__pcnts,0x4000 \ +// RUN: -Wl,-sectalign,__DATA,__pdata,0x4000 + +// Create a 'profile' using mmap() and validate it. +// RUN: %run %t create %t.tmpfile +// RUN: %run %t validate %t.tmpfile + +#include +#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; + } + + // Verify that the file length is: sizeof(counters) + sizeof(data). + const size_t num_bytes = cnts_len + data_len; + int buf[num_bytes]; + if (num_bytes != read(fd, &buf, num_bytes)) { + perror("read"); + return EXIT_FAILURE; + } + + // Verify the values of counters 1..9 (i.e. that the mmap() worked). + for (int i = 0; i < 10; ++i) { + if (buf[i] != i) { + fprintf(stderr, + "validate_tmpfile: Expected '%d' at pos=%d, but got '%d' instead.\n", + i, i, buf[i]); + return EXIT_FAILURE; + } + } + + // Verify that the rest of the counters (after counter 9) are 0. + const int num_cnts = 0x4000 / sizeof(int); + for (int i = 10; i < num_cnts; ++i) { + if (buf[i] != 0) { + fprintf(stderr, + "validate_tmpfile: Expected '%d' at pos=%d, but got '%d' instead.\n", + 0, i, buf[i]); + return EXIT_FAILURE; + } + } + + // Verify that the data written after the counters is equal to the "data[]" + // array (i.e. {1, 2, 3}). + for (int i = num_cnts; i < num_cnts + 3; ++i) { + if (buf[i] != (i - num_cnts + 1)) { + fprintf(stderr, + "validate_tmpfile: Expected '%d' at pos=%d, but got '%d' instead.\n", + i - num_cnts + 1, i, buf[i]); + return EXIT_FAILURE; + } + } + + // Intentionally do not close(). + return EXIT_SUCCESS; +} + +int main(int argc, char **argv) { + intptr_t cnts_start_int = (intptr_t)&cnts_start; + intptr_t data_start_int = (intptr_t)&data_start; + int pagesz = getpagesize(); + + if (cnts_start_int % pagesz != 0) { + fprintf(stderr, "__pcnts is not page-aligned: 0x%lx.\n", cnts_start_int); + return EXIT_FAILURE; + } + if (data_start_int % pagesz != 0) { + fprintf(stderr, "__pdata is not page-aligned: 0x%lx.\n", data_start_int); + return EXIT_FAILURE; + } + if (cnts_start_int + 0x4000 != data_start_int) { + fprintf(stderr, "__pdata not ordered after __pcnts.\n"); + return EXIT_FAILURE; + } + + char *action = argv[1]; + char *path = argv[2]; + if (0 == strcmp(action, "create")) + return create_tmpfile(path); + else if (0 == strcmp(action, "validate")) + return validate_tmpfile(path); + else + return EXIT_FAILURE; +} diff --git a/compiler-rt/test/profile/ContinuousSyncMode/lit.local.cfg.py b/compiler-rt/test/profile/ContinuousSyncMode/lit.local.cfg.py new file mode 100644 index 0000000000000..0918f09cdaa88 --- /dev/null +++ b/compiler-rt/test/profile/ContinuousSyncMode/lit.local.cfg.py @@ -0,0 +1,18 @@ +import subprocess + +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +# As this has not been tested extensively on non-Darwin platforms, +# only Darwin support is enabled for the moment. However, continuous mode +# may "just work" without modification on Linux and other UNIX-likes (AIUI +# the default value for the GNU linker's `--section-alignment` flag is +# 0x1000, which is the size of a page on many systems). +# +# Please add supported configs to this list. +if root.host_os not in ['Darwin']: + config.unsupported = True diff --git a/compiler-rt/test/profile/ContinuousSyncMode/multiple-DSOs.c b/compiler-rt/test/profile/ContinuousSyncMode/multiple-DSOs.c new file mode 100644 index 0000000000000..a54c9af6828d4 --- /dev/null +++ b/compiler-rt/test/profile/ContinuousSyncMode/multiple-DSOs.c @@ -0,0 +1,35 @@ +// RUN: echo "void dso1(void) {}" > %t.dso1.c +// RUN: echo "void dso2(void) {}" > %t.dso2.c +// RUN: %clang_pgogen -dynamiclib -o %t.dso1.dylib %t.dso1.c +// RUN: %clang_pgogen -dynamiclib -o %t.dso2.dylib %t.dso2.c +// RUN: %clang_pgogen -o %t.exe %s %t.dso1.dylib %t.dso2.dylib +// RUN: env LLVM_PROFILE_FILE="%c%t.profraw" %run %t.exe +// RUN: llvm-profdata show --counts --all-functions %t.profraw | FileCheck %s + +// CHECK-LABEL: Counters: +// CHECK-NEXT: dso1: +// CHECK-NEXT: Hash: 0x{{.*}} +// CHECK-NEXT: Counters: 1 +// CHECK-NEXT: Block counts: [1] +// CHECK-NEXT: dso2: +// CHECK-NEXT: Hash: 0x{{.*}} +// CHECK-NEXT: Counters: 1 +// CHECK-NEXT: Block counts: [1] +// CHECK-NEXT: main: +// CHECK-NEXT: Hash: 0x{{.*}} +// CHECK-NEXT: Counters: 1 +// CHECK-NEXT: Block counts: [1] +// CHECK-NEXT: Instrumentation level: IR +// CHECK-NEXT: Functions shown: 3 +// CHECK-NEXT: Total functions: 3 +// CHECK-NEXT: Maximum function count: 1 +// CHECK-NEXT: Maximum internal block count: 0 + +void dso1(void); +void dso2(void); + +int main() { + dso1(); + dso2(); + return 0; +} diff --git a/compiler-rt/test/profile/ContinuousSyncMode/pid-substitution.c b/compiler-rt/test/profile/ContinuousSyncMode/pid-substitution.c new file mode 100644 index 0000000000000..64ba0fbdb7a50 --- /dev/null +++ b/compiler-rt/test/profile/ContinuousSyncMode/pid-substitution.c @@ -0,0 +1,34 @@ +// RUN: rm -rf %t.dir && mkdir -p %t.dir +// RUN: %clang_pgogen -o %t.exe %s +// +// Note: %%p is needed here, not %p, because of lit's path substitution. +// RUN: env LLVM_PROFILE_FILE="%t.dir/%c-%%p" %run %t.exe + +#include +#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; +} diff --git a/compiler-rt/test/profile/ContinuousSyncMode/set-file-object.c b/compiler-rt/test/profile/ContinuousSyncMode/set-file-object.c new file mode 100644 index 0000000000000..ac3be3b5237cf --- /dev/null +++ b/compiler-rt/test/profile/ContinuousSyncMode/set-file-object.c @@ -0,0 +1,32 @@ +// RUN: %clang_pgogen -o %t.exe %s +// RUN: env LLVM_PROFILE_FILE="%c%t.profraw" %run %t.exe %t.bad 2>&1 | FileCheck %s + +// CHECK: __llvm_profile_set_file_object(fd={{[0-9]+}}) not supported +// CHECK: Profile data not written to file: already written. + +#include + +extern int __llvm_profile_is_continuous_mode_enabled(void); +extern void __llvm_profile_set_file_object(FILE *, int); +extern int __llvm_profile_write_file(void); + +int main(int argc, char **argv) { + if (!__llvm_profile_is_continuous_mode_enabled()) + return 1; + + FILE *f = fopen(argv[1], "a+b"); + if (!f) + return 1; + + __llvm_profile_set_file_object(f, 0); // Try to set the file to "%t.bad". + + if (__llvm_profile_write_file() != 0) + return 1; + + f = fopen(argv[1], "r"); + if (!f) + return 1; + + fseek(f, 0, SEEK_END); + return ftell(f); // Check that the "%t.bad" is empty. +} diff --git a/compiler-rt/test/profile/ContinuousSyncMode/set-filename.c b/compiler-rt/test/profile/ContinuousSyncMode/set-filename.c new file mode 100644 index 0000000000000..2e6a78950a00b --- /dev/null +++ b/compiler-rt/test/profile/ContinuousSyncMode/set-filename.c @@ -0,0 +1,17 @@ +// RUN: %clang_pgogen -o %t.exe %s +// RUN: env LLVM_PROFILE_FILE="%c%t.profraw" %run %t.exe %t.profraw %t.bad + +#include + +extern int __llvm_profile_is_continuous_mode_enabled(void); +extern void __llvm_profile_set_filename(const char *); +extern const char *__llvm_profile_get_filename(); + +int main(int argc, char **argv) { + if (!__llvm_profile_is_continuous_mode_enabled()) + return 1; + + __llvm_profile_set_filename(argv[2]); // Try to set the filename to "%t.bad". + const char *Filename = __llvm_profile_get_filename(); + return strcmp(Filename, argv[1]); // Check that the filename is "%t.profraw". +} diff --git a/compiler-rt/test/profile/Inputs/instrprof-get-filename-dso.c b/compiler-rt/test/profile/Inputs/instrprof-get-filename-dso.c new file mode 100644 index 0000000000000..2709434003451 --- /dev/null +++ b/compiler-rt/test/profile/Inputs/instrprof-get-filename-dso.c @@ -0,0 +1,5 @@ +const char *__llvm_profile_get_filename(void); + +const char *get_filename_from_DSO(void) { + return __llvm_profile_get_filename(); +} diff --git a/compiler-rt/test/profile/Posix/instrprof-gcov-fork.test b/compiler-rt/test/profile/Posix/instrprof-gcov-fork.test index 5a406fda4b671..31b6a23be49cd 100644 --- a/compiler-rt/test/profile/Posix/instrprof-gcov-fork.test +++ b/compiler-rt/test/profile/Posix/instrprof-gcov-fork.test @@ -1,4 +1,5 @@ UNSUPPORTED: linux +UNSUPPORTED: darwin RUN: mkdir -p %t.d RUN: cd %t.d diff --git a/compiler-rt/test/profile/Posix/instrprof-get-filename-merge-mode.c b/compiler-rt/test/profile/Posix/instrprof-get-filename-merge-mode.c new file mode 100644 index 0000000000000..7e26e3e6b5dd3 --- /dev/null +++ b/compiler-rt/test/profile/Posix/instrprof-get-filename-merge-mode.c @@ -0,0 +1,18 @@ +// Test __llvm_profile_get_filename when the on-line merging mode is enabled. +// +// RUN: %clang_pgogen -fPIC -shared -o %t.dso %p/../Inputs/instrprof-get-filename-dso.c +// RUN: %clang_pgogen -o %t %s %t.dso +// RUN: env LLVM_PROFILE_FILE="%t-%m.profraw" %run %t + +#include + +const char *__llvm_profile_get_filename(void); +extern const char *get_filename_from_DSO(void); + +int main(int argc, const char *argv[]) { + const char *filename1 = __llvm_profile_get_filename(); + const char *filename2 = get_filename_from_DSO(); + + // Exit with code 1 if the two filenames are the same. + return strcmp(filename1, filename2) == 0; +} diff --git a/compiler-rt/test/profile/Posix/instrprof-set-filename-shared.test b/compiler-rt/test/profile/Posix/instrprof-set-filename-shared.test deleted file mode 100644 index 439c6c0dda8fa..0000000000000 --- a/compiler-rt/test/profile/Posix/instrprof-set-filename-shared.test +++ /dev/null @@ -1,8 +0,0 @@ -# Test that __llvm_profile_set_filename is honored by shared libary too. -RUN: mkdir -p %t.d -RUN: %clang_profgen=%t.shared.profraw -fPIC -shared -o %t.d/t.shared %S/../Inputs/instrprof-dlopen-func.c -RUN: %clang_profgen -DCALL_SHARED -o %t.m -O3 -rpath %t.d %t.d/t.shared %S/../instrprof-set-filename.c -RUN: %run %t.m %t.main.profraw -RUN: llvm-profdata show %t.main.profraw | FileCheck --check-prefix=SHARED %s - -# SHARED: Total functions: 2 diff --git a/compiler-rt/test/profile/instrprof-set-file-object-merging.c b/compiler-rt/test/profile/instrprof-set-file-object-merging.c new file mode 100644 index 0000000000000..3f71a8103561a --- /dev/null +++ b/compiler-rt/test/profile/instrprof-set-file-object-merging.c @@ -0,0 +1,43 @@ +// Test that the specified output merges the profiling data. +// Run the program twice so that the counters accumulate. +// RUN: %clang -fprofile-instr-generate -fcoverage-mapping -o %t %s +// RUN: %run %t %t.merging.profraw +// RUN: %run %t %t.merging.profraw +// RUN: test -f %t.merging.profraw +// RUN: llvm-profdata merge -o %t.merging.profdata %t.merging.profraw +// RUN: llvm-cov show -instr-profile %t.merging.profdata %t | FileCheck %s --match-full-lines +// RUN: rm %t.merging.profdata %t.merging.profraw +#include + +extern void __llvm_profile_set_file_object(FILE *, int); + +int main(int argc, const char *argv[]) { + if (argc < 2) + return 1; + + FILE *F = fopen(argv[1], "r+b"); + if (!F) { + // File might not exist, try opening with truncation + F = fopen(argv[1], "w+b"); + } + __llvm_profile_set_file_object(F, 1); + + return 0; +} +// CHECK: 10| |#include +// CHECK: 11| | +// CHECK: 12| |extern void __llvm_profile_set_file_object(FILE *, int); +// CHECK: 13| | +// CHECK: 14| 2|int main(int argc, const char *argv[]) { +// CHECK: 15| 2| if (argc < 2) +// CHECK: 16| 0| return 1; +// CHECK: 17| 2| +// CHECK: 18| 2| FILE *F = fopen(argv[1], "r+b"); +// CHECK: 19| 2| if (!F) { +// CHECK: 20| 1| // File might not exist, try opening with truncation +// CHECK: 21| 1| F = fopen(argv[1], "w+b"); +// CHECK: 22| 1| } +// CHECK: 23| 2| __llvm_profile_set_file_object(F, 1); +// CHECK: 24| 2| +// CHECK: 25| 2| return 0; +// CHECK: 26| 2|} diff --git a/compiler-rt/test/profile/instrprof-set-file-object.c b/compiler-rt/test/profile/instrprof-set-file-object.c new file mode 100644 index 0000000000000..813130007f04e --- /dev/null +++ b/compiler-rt/test/profile/instrprof-set-file-object.c @@ -0,0 +1,31 @@ +// Test that the specified output has profiling data. +// RUN: %clang -fprofile-instr-generate -fcoverage-mapping -o %t %s +// RUN: %run %t %t.file.profraw +// RUN: test -f %t.file.profraw +// RUN: llvm-profdata merge -o %t.file.profdata %t.file.profraw +// RUN: llvm-cov show -instr-profile %t.file.profdata %t | FileCheck %s --match-full-lines +// RUN: rm %t.file.profraw %t.file.profdata +#include + +extern void __llvm_profile_set_file_object(FILE *, int); + +int main(int argc, const char *argv[]) { + if (argc < 2) + return 1; + + FILE *F = fopen(argv[1], "w+b"); + __llvm_profile_set_file_object(F, 0); + return 0; +} +// CHECK: 8| |#include +// CHECK: 9| | +// CHECK: 10| |extern void __llvm_profile_set_file_object(FILE *, int); +// CHECK: 11| | +// CHECK: 12| 1|int main(int argc, const char *argv[]) { +// CHECK: 13| 1| if (argc < 2) +// CHECK: 14| 0| return 1; +// CHECK: 15| 1| +// CHECK: 16| 1| FILE *F = fopen(argv[1], "w+b"); +// CHECK: 17| 1| __llvm_profile_set_file_object(F, 0); +// CHECK: 18| 1| return 0; +// CHECK: 19| 1|} diff --git a/compiler-rt/test/profile/instrprof-write-file.c b/compiler-rt/test/profile/instrprof-write-file.c index af008ed9439e8..e2f5e112e0643 100644 --- a/compiler-rt/test/profile/instrprof-write-file.c +++ b/compiler-rt/test/profile/instrprof-write-file.c @@ -1,3 +1,4 @@ +// RUN: rm -rf %t1.profraw %t2.profraw // RUN: %clang_profgen -o %t -O3 %s // RUN: env LLVM_PROFILE_FILE=%t1.profraw %run %t %t2.profraw // RUN: llvm-profdata merge -o %t1.profdata %t1.profraw diff --git a/lldb/cmake/modules/AddLLDB.cmake b/lldb/cmake/modules/AddLLDB.cmake index a0b007c23d01f..c4edd735dce44 100644 --- a/lldb/cmake/modules/AddLLDB.cmake +++ b/lldb/cmake/modules/AddLLDB.cmake @@ -260,13 +260,15 @@ function(lldb_add_post_install_steps_darwin name install_prefix) install(CODE "execute_process(COMMAND xcrun dsymutil -o=${dsym_name} ${buildtree_name})" COMPONENT ${name}) - # Strip distribution binary with -ST (removing debug symbol table entries and - # Swift symbols). Avoid CMAKE_INSTALL_DO_STRIP and llvm_externalize_debuginfo() - # as they can't be configured sufficiently. - set(installtree_name "\$ENV\{DESTDIR\}${install_prefix}/${bundle_subdir}${output_name}") - install(CODE "message(STATUS \"Stripping: ${installtree_name}\")" COMPONENT ${name}) - install(CODE "execute_process(COMMAND xcrun strip -ST ${installtree_name})" - COMPONENT ${name}) + if(NOT LLDB_SKIP_STRIP) + # Strip distribution binary with -ST (removing debug symbol table entries and + # Swift symbols). Avoid CMAKE_INSTALL_DO_STRIP and llvm_externalize_debuginfo() + # as they can't be configured sufficiently. + set(installtree_name "\$ENV\{DESTDIR\}${install_prefix}/${bundle_subdir}${output_name}") + install(CODE "message(STATUS \"Stripping: ${installtree_name}\")" COMPONENT ${name}) + install(CODE "execute_process(COMMAND xcrun strip -ST ${installtree_name})" + COMPONENT ${name}) + endif() endfunction() # CMake's set_target_properties() doesn't allow to pass lists for RPATH diff --git a/lldb/cmake/modules/LLDBConfig.cmake b/lldb/cmake/modules/LLDBConfig.cmake index 9f5c9ccc03ba2..de21ce1acadb8 100644 --- a/lldb/cmake/modules/LLDBConfig.cmake +++ b/lldb/cmake/modules/LLDBConfig.cmake @@ -51,6 +51,7 @@ option(LLDB_USE_ENTITLEMENTS "When codesigning, use entitlements if available" O option(LLDB_BUILD_FRAMEWORK "Build LLDB.framework (Darwin only)" OFF) option(LLDB_NO_INSTALL_DEFAULT_RPATH "Disable default RPATH settings in binaries" OFF) option(LLDB_USE_SYSTEM_DEBUGSERVER "Use the system's debugserver for testing (Darwin only)." OFF) +option(LLDB_SKIP_STRIP "Whether to skip stripping of binaries when installing lldb." OFF) if(LLDB_BUILD_FRAMEWORK) if(NOT APPLE) diff --git a/lldb/include/lldb/Symbol/Function.h b/lldb/include/lldb/Symbol/Function.h index f68a16705d935..b0a7a0d184669 100644 --- a/lldb/include/lldb/Symbol/Function.h +++ b/lldb/include/lldb/Symbol/Function.h @@ -402,6 +402,11 @@ class Function : public UserID, public SymbolContextScope { /// return None. llvm::MutableArrayRef GetTailCallingEdges(); + /// Get the outgoing call edge from this function which has the given return + /// address \p return_pc, or return nullptr. Note that this will not return a + /// tail-calling edge. + CallEdge *GetCallEdgeForReturnAddress(lldb::addr_t return_pc, Target &target); + /// Get accessor for the block list. /// /// \return diff --git a/lldb/include/lldb/Utility/Status.h b/lldb/include/lldb/Utility/Status.h index 71a9879708a35..e6a0a8e7fce1b 100644 --- a/lldb/include/lldb/Utility/Status.h +++ b/lldb/include/lldb/Utility/Status.h @@ -212,4 +212,11 @@ template <> struct format_provider { }; } +#define LLDB_ERRORF(status, fmt, ...) \ + do { \ + if (status) { \ + (status)->SetErrorStringWithFormat((fmt), __VA_ARGS__); \ + } \ + } while (0); + #endif // #ifndef LLDB_UTILITY_STATUS_H diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py index 48dc81780e9b4..3f1a30fb34459 100644 --- a/lldb/packages/Python/lldbsuite/test/lldbtest.py +++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py @@ -2208,12 +2208,16 @@ def filecheck( self, command, check_file, - filecheck_options = ''): + filecheck_options = '', + expect_cmd_failure = False): # Run the command. self.runCmd( command, + check=(not expect_cmd_failure), msg="FileCheck'ing result of `{0}`".format(command)) + self.assertTrue((not expect_cmd_failure) == self.res.Succeeded()) + # Get the error text if there was an error, and the regular text if not. output = self.res.GetOutput() if self.res.Succeeded() \ else self.res.GetError() diff --git a/lldb/source/Symbol/Function.cpp b/lldb/source/Symbol/Function.cpp index 28ce4fdd4ad40..9ba178f2563c1 100644 --- a/lldb/source/Symbol/Function.cpp +++ b/lldb/source/Symbol/Function.cpp @@ -276,6 +276,20 @@ llvm::MutableArrayRef Function::GetTailCallingEdges() { }); } +CallEdge *Function::GetCallEdgeForReturnAddress(addr_t return_pc, + Target &target) { + auto edges = GetCallEdges(); + auto edge_it = + std::lower_bound(edges.begin(), edges.end(), return_pc, + [&](const CallEdge &edge, addr_t pc) { + return edge.GetReturnPCAddress(*this, target) < pc; + }); + if (edge_it == edges.end() || + edge_it->GetReturnPCAddress(*this, target) != return_pc) + return nullptr; + return &const_cast(*edge_it); +} + Block &Function::GetBlock(bool can_create) { if (!m_block.BlockInfoHasBeenParsed() && can_create) { ModuleSP module_sp = CalculateSymbolContextModule(); diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp index 69445624a53c8..fbced5fd132d9 100644 --- a/lldb/source/Target/StackFrameList.cpp +++ b/lldb/source/Target/StackFrameList.cpp @@ -250,26 +250,19 @@ static void FindInterveningFrames(Function &begin, Function &end, begin.GetDisplayName(), end.GetDisplayName(), return_pc); // Find a non-tail calling edge with the correct return PC. - auto first_level_edges = begin.GetCallEdges(); if (log) - for (const CallEdge &edge : first_level_edges) + for (const CallEdge &edge : begin.GetCallEdges()) LLDB_LOG(log, "FindInterveningFrames: found call with retn-PC = {0:x}", edge.GetReturnPCAddress(begin, target)); - auto first_edge_it = std::lower_bound( - first_level_edges.begin(), first_level_edges.end(), return_pc, - [&](const CallEdge &edge, addr_t target_pc) { - return edge.GetReturnPCAddress(begin, target) < target_pc; - }); - if (first_edge_it == first_level_edges.end() || - first_edge_it->GetReturnPCAddress(begin, target) != return_pc) { + CallEdge *first_edge = begin.GetCallEdgeForReturnAddress(return_pc, target); + if (!first_edge) { LLDB_LOG(log, "No call edge outgoing from {0} with retn-PC == {1:x}", begin.GetDisplayName(), return_pc); return; } - CallEdge &first_edge = const_cast(*first_edge_it); // The first callee may not be resolved, or there may be nothing to fill in. - Function *first_callee = first_edge.GetCallee(images); + Function *first_callee = first_edge->GetCallee(images); if (!first_callee) { LLDB_LOG(log, "Could not resolve callee"); return; diff --git a/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt b/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt index 7ad4a06a4d361..cf08985ed6f27 100644 --- a/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt +++ b/lldb/tools/debugserver/source/MacOSX/CMakeLists.txt @@ -1,14 +1,32 @@ -if("${CMAKE_OSX_ARCHITECTURES}" MATCHES ".*arm.*") +# The debugserver build needs to conditionally include files depending on the +# target architecture. +# +# Switch on the architecture specified by LLVM_DEFAULT_TARGET_TRIPLE, as +# the llvm and swift build systems use this variable to identify the +# target (the latter, indirectly, through LLVM_HOST_TRIPLE). +# +# It would be possible to switch on CMAKE_OSX_ARCHITECTURES, but the swift +# build does not provide it, preferring instead to pass arch-specific +# CFLAGS etc explicitly. Switching on LLVM_HOST_TRIPLE is also an option, +# but it breaks down when cross-compiling. + +if(LLVM_DEFAULT_TARGET_TRIPLE) + string(REGEX MATCH "^[^-]*" LLDB_DEBUGSERVER_ARCH ${LLVM_DEFAULT_TARGET_TRIPLE}) +else() + set(LLDB_DEBUGSERVER_ARCH ${CMAKE_OSX_ARCHITECTURES}) +endif() + +if("${LLDB_DEBUGSERVER_ARCH}" MATCHES ".*arm.*") list(APPEND SOURCES arm/DNBArchImpl.cpp arm64/DNBArchImplARM64.cpp) include_directories(${CURRENT_SOURCE_DIR}/arm ${CURRENT_SOURCE_DIR}/arm64) endif() -if(NOT CMAKE_OSX_ARCHITECTURES OR "${CMAKE_OSX_ARCHITECTURES}" MATCHES ".*86.*") +if(NOT LLDB_DEBUGSERVER_ARCH OR "${LLDB_DEBUGSERVER_ARCH}" MATCHES ".*86.*") list(APPEND SOURCES i386/DNBArchImplI386.cpp x86_64/DNBArchImplX86_64.cpp) include_directories(${CURRENT_SOURCE_DIR}/i386 ${CURRENT_SOURCE_DIR}/x86_64) endif() -if("${CMAKE_OSX_ARCHITECTURES}" MATCHES ".*ppc.*") +if("${LLDB_DEBUGSERVER_ARCH}" MATCHES ".*ppc.*") list(APPEND SOURCES ppc/DNBArchImpl.cpp) include_directories(${CURRENT_SOURCE_DIR}/ppc) endif() diff --git a/llvm/include/llvm/ProfileData/InstrProfData.inc b/llvm/include/llvm/ProfileData/InstrProfData.inc index 749781b9ac2d0..99f41d8fef077 100644 --- a/llvm/include/llvm/ProfileData/InstrProfData.inc +++ b/llvm/include/llvm/ProfileData/InstrProfData.inc @@ -130,7 +130,9 @@ INSTR_PROF_VALUE_NODE(PtrToNodeT, llvm::Type::getInt8PtrTy(Ctx), Next, \ INSTR_PROF_RAW_HEADER(uint64_t, Magic, __llvm_profile_get_magic()) INSTR_PROF_RAW_HEADER(uint64_t, Version, __llvm_profile_get_version()) INSTR_PROF_RAW_HEADER(uint64_t, DataSize, DataSize) +INSTR_PROF_RAW_HEADER(uint64_t, PaddingBytesBeforeCounters, PaddingBytesBeforeCounters) INSTR_PROF_RAW_HEADER(uint64_t, CountersSize, CountersSize) +INSTR_PROF_RAW_HEADER(uint64_t, PaddingBytesAfterCounters, PaddingBytesAfterCounters) INSTR_PROF_RAW_HEADER(uint64_t, NamesSize, NamesSize) INSTR_PROF_RAW_HEADER(uint64_t, CountersDelta, (uintptr_t)CountersBegin) INSTR_PROF_RAW_HEADER(uint64_t, NamesDelta, (uintptr_t)NamesBegin) @@ -628,7 +630,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure, (uint64_t)'f' << 16 | (uint64_t)'R' << 8 | (uint64_t)129 /* Raw profile format version (start from 1). */ -#define INSTR_PROF_RAW_VERSION 4 +#define INSTR_PROF_RAW_VERSION 5 /* Indexed profile format version (start from 1). */ #define INSTR_PROF_INDEX_VERSION 5 /* Coverage mapping format vresion (start from 0). */ @@ -742,7 +744,7 @@ typedef struct InstrProfValueData { #endif /* INSTR_PROF_DATA_INC */ #ifndef INSTR_ORDER_FILE_INC -// The maximal # of functions: 128*1024 (the buffer size will be 128*4 KB). +/* The maximal # of functions: 128*1024 (the buffer size will be 128*4 KB). */ #define INSTR_ORDER_FILE_BUFFER_SIZE 131072 #define INSTR_ORDER_FILE_BUFFER_BITS 17 #define INSTR_ORDER_FILE_BUFFER_MASK 0x1ffff diff --git a/llvm/lib/ProfileData/InstrProfReader.cpp b/llvm/lib/ProfileData/InstrProfReader.cpp index c80d94ea6e7e1..0c80fe6a2c732 100644 --- a/llvm/lib/ProfileData/InstrProfReader.cpp +++ b/llvm/lib/ProfileData/InstrProfReader.cpp @@ -362,7 +362,9 @@ Error RawInstrProfReader::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::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(&Header); diff --git a/llvm/test/tools/llvm-profdata/Inputs/c-general.profraw b/llvm/test/tools/llvm-profdata/Inputs/c-general.profraw index 332d11b140f52..c453d806770d8 100644 Binary files a/llvm/test/tools/llvm-profdata/Inputs/c-general.profraw and b/llvm/test/tools/llvm-profdata/Inputs/c-general.profraw differ diff --git a/llvm/test/tools/llvm-profdata/Inputs/compressed.profraw b/llvm/test/tools/llvm-profdata/Inputs/compressed.profraw index 0cfaa07b19542..dabb8cb4d4c7a 100644 Binary files a/llvm/test/tools/llvm-profdata/Inputs/compressed.profraw and b/llvm/test/tools/llvm-profdata/Inputs/compressed.profraw differ diff --git a/llvm/test/tools/llvm-profdata/Inputs/malformed-ptr-to-counter-array.profraw b/llvm/test/tools/llvm-profdata/Inputs/malformed-ptr-to-counter-array.profraw deleted file mode 100644 index 72fddd76fc6e3..0000000000000 Binary files a/llvm/test/tools/llvm-profdata/Inputs/malformed-ptr-to-counter-array.profraw and /dev/null differ diff --git a/llvm/test/tools/llvm-profdata/c-general.test b/llvm/test/tools/llvm-profdata/c-general.test index ed4e61962e425..d4ed384ad6032 100644 --- a/llvm/test/tools/llvm-profdata/c-general.test +++ b/llvm/test/tools/llvm-profdata/c-general.test @@ -5,7 +5,7 @@ $ SRC=path/to/llvm $ CFE=$SRC/tools/clang $ TESTDIR=$SRC/test/tools/llvm-profdata $ CFE_TESTDIR=$CFE/test/Profile -$ clang -o a.out -fprofile-instr-generate $CFE_TESTDIR/c-general.c +$ clang -o a.out -fprofile-instr-generate $CFE_TESTDIR/c-general.c -mllvm -enable-name-compression=false $ LLVM_PROFILE_FILE=$TESTDIR/Inputs/c-general.profraw ./a.out RUN: llvm-profdata show %p/Inputs/c-general.profraw -o - | FileCheck %s @@ -14,7 +14,7 @@ RUN: llvm-profdata show %p/Inputs/c-general.profraw -o - --function=switches | F SWITCHES-LABEL: Counters: SWITCHES-NEXT: switches: -SWITCHES-NEXT: Hash: 0x2618e4f23f2e8daa +SWITCHES-NEXT: Hash: 0xa50a07f391ae4be5 SWITCHES-NEXT: Counters: 19 SWITCHES-NEXT: Function count: 1 SWITCHES-LABEL: Functions shown: 1 diff --git a/llvm/test/tools/llvm-profdata/malformed-ptr-to-counter-array.test b/llvm/test/tools/llvm-profdata/malformed-ptr-to-counter-array.test index 24a68e35fad9a..bad4eb301829f 100644 --- a/llvm/test/tools/llvm-profdata/malformed-ptr-to-counter-array.test +++ b/llvm/test/tools/llvm-profdata/malformed-ptr-to-counter-array.test @@ -1,5 +1,54 @@ -REQUIRES: zlib +// Header +// +// 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, CountersSize, CountersSize) +// 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) +// INSTR_PROF_RAW_HEADER(uint64_t, ValueKindLast, IPVK_Last) -RUN: not llvm-profdata merge -o /dev/null %p/Inputs/malformed-ptr-to-counter-array.profraw 2>&1 | FileCheck %s +RUN: printf '\201rforpl\377' > %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 +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +// Data Section +// +// struct ProfData { +// #define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) \ +// Type Name; +// #include "llvm/ProfileData/InstrProfData.inc" +// }; + +RUN: printf '\067\265\035\031\112\165\023\344' >> %t.profraw +RUN: printf '\02\0\0\0\0\0\0\0' >> %t.profraw + +// Note: The CounterPtr here is off-by-one. +// +// Octal '\11' is 9 in decimal: this should push CounterOffset to 1. As there are two counters, +// the profile reader should error out. +RUN: printf '\11\0\6\0\1\0\0\0' >> %t.profraw + +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\02\0\0\0\0\0\0\0' >> %t.profraw + +// Counter Section + +RUN: printf '\067\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\101\0\0\0\0\0\0\0' >> %t.profraw + +// Name Section + +RUN: printf '\3\0bar\0\0\0' >> %t.profraw + +RUN: not llvm-profdata merge -o /dev/null %t.profraw 2>&1 | FileCheck %s CHECK: Malformed instrumentation profile data diff --git a/llvm/test/tools/llvm-profdata/raw-32-bits-be.test b/llvm/test/tools/llvm-profdata/raw-32-bits-be.test index d6e1daa1655cc..cebd25a07f4a1 100644 --- a/llvm/test/tools/llvm-profdata/raw-32-bits-be.test +++ b/llvm/test/tools/llvm-profdata/raw-32-bits-be.test @@ -1,7 +1,9 @@ RUN: printf '\377lprofR\201' > %t -RUN: printf '\0\0\0\0\0\0\0\4' >> %t +RUN: printf '\0\0\0\0\0\0\0\5' >> %t RUN: printf '\0\0\0\0\0\0\0\2' >> %t +RUN: printf '\0\0\0\0\0\0\0\0' >> %t RUN: printf '\0\0\0\0\0\0\0\3' >> %t +RUN: printf '\0\0\0\0\0\0\0\0' >> %t RUN: printf '\0\0\0\0\0\0\0\20' >> %t RUN: printf '\0\0\0\0\1\0\0\0' >> %t RUN: printf '\0\0\0\0\2\0\0\0' >> %t diff --git a/llvm/test/tools/llvm-profdata/raw-32-bits-le.test b/llvm/test/tools/llvm-profdata/raw-32-bits-le.test index cd36aafc9ad9e..a176f80ec95ac 100644 --- a/llvm/test/tools/llvm-profdata/raw-32-bits-le.test +++ b/llvm/test/tools/llvm-profdata/raw-32-bits-le.test @@ -1,7 +1,9 @@ RUN: printf '\201Rforpl\377' > %t -RUN: printf '\4\0\0\0\0\0\0\0' >> %t +RUN: printf '\5\0\0\0\0\0\0\0' >> %t RUN: printf '\2\0\0\0\0\0\0\0' >> %t +RUN: printf '\0\0\0\0\0\0\0\0' >> %t RUN: printf '\3\0\0\0\0\0\0\0' >> %t +RUN: printf '\0\0\0\0\0\0\0\0' >> %t RUN: printf '\20\0\0\0\0\0\0\0' >> %t RUN: printf '\0\0\0\1\0\0\0\0' >> %t RUN: printf '\0\0\0\2\0\0\0\0' >> %t diff --git a/llvm/test/tools/llvm-profdata/raw-64-bits-be.test b/llvm/test/tools/llvm-profdata/raw-64-bits-be.test index 75cc84d68862d..c679207102e40 100644 --- a/llvm/test/tools/llvm-profdata/raw-64-bits-be.test +++ b/llvm/test/tools/llvm-profdata/raw-64-bits-be.test @@ -1,7 +1,9 @@ RUN: printf '\377lprofr\201' > %t -RUN: printf '\0\0\0\0\0\0\0\4' >> %t +RUN: printf '\0\0\0\0\0\0\0\5' >> %t RUN: printf '\0\0\0\0\0\0\0\2' >> %t +RUN: printf '\0\0\0\0\0\0\0\0' >> %t RUN: printf '\0\0\0\0\0\0\0\3' >> %t +RUN: printf '\0\0\0\0\0\0\0\0' >> %t RUN: printf '\0\0\0\0\0\0\0\20' >> %t RUN: printf '\0\0\0\1\0\4\0\0' >> %t RUN: printf '\0\0\0\2\0\4\0\0' >> %t diff --git a/llvm/test/tools/llvm-profdata/raw-64-bits-le.test b/llvm/test/tools/llvm-profdata/raw-64-bits-le.test index d8a9c9a7586c7..8e179a8716255 100644 --- a/llvm/test/tools/llvm-profdata/raw-64-bits-le.test +++ b/llvm/test/tools/llvm-profdata/raw-64-bits-le.test @@ -1,7 +1,9 @@ RUN: printf '\201rforpl\377' > %t -RUN: printf '\4\0\0\0\0\0\0\0' >> %t +RUN: printf '\5\0\0\0\0\0\0\0' >> %t RUN: printf '\2\0\0\0\0\0\0\0' >> %t +RUN: printf '\0\0\0\0\0\0\0\0' >> %t RUN: printf '\3\0\0\0\0\0\0\0' >> %t +RUN: printf '\0\0\0\0\0\0\0\0' >> %t RUN: printf '\20\0\0\0\0\0\0\0' >> %t RUN: printf '\0\0\4\0\1\0\0\0' >> %t RUN: printf '\0\0\4\0\2\0\0\0' >> %t diff --git a/llvm/test/tools/llvm-profdata/raw-two-profiles.test b/llvm/test/tools/llvm-profdata/raw-two-profiles.test index a377375c1768c..9cd8a8c7b00ee 100644 --- a/llvm/test/tools/llvm-profdata/raw-two-profiles.test +++ b/llvm/test/tools/llvm-profdata/raw-two-profiles.test @@ -1,7 +1,9 @@ RUN: printf '\201rforpl\377' > %t-foo.profraw -RUN: printf '\4\0\0\0\0\0\0\0' >> %t-foo.profraw +RUN: printf '\5\0\0\0\0\0\0\0' >> %t-foo.profraw RUN: printf '\1\0\0\0\0\0\0\0' >> %t-foo.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t-foo.profraw RUN: printf '\1\0\0\0\0\0\0\0' >> %t-foo.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t-foo.profraw RUN: printf '\10\0\0\0\0\0\0\0' >> %t-foo.profraw RUN: printf '\0\0\4\0\1\0\0\0' >> %t-foo.profraw RUN: printf '\0\0\4\0\2\0\0\0' >> %t-foo.profraw @@ -18,9 +20,11 @@ RUN: printf '\023\0\0\0\0\0\0\0' >> %t-foo.profraw RUN: printf '\3\0foo\0\0\0' >> %t-foo.profraw RUN: printf '\201rforpl\377' > %t-bar.profraw -RUN: printf '\4\0\0\0\0\0\0\0' >> %t-bar.profraw +RUN: printf '\5\0\0\0\0\0\0\0' >> %t-bar.profraw RUN: printf '\1\0\0\0\0\0\0\0' >> %t-bar.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t-bar.profraw RUN: printf '\2\0\0\0\0\0\0\0' >> %t-bar.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t-bar.profraw RUN: printf '\10\0\0\0\0\0\0\0' >> %t-bar.profraw RUN: printf '\0\0\6\0\1\0\0\0' >> %t-bar.profraw RUN: printf '\0\0\6\0\2\0\0\0' >> %t-bar.profraw