Index: include/llvm/ProfileData/InstrProf.h =================================================================== --- include/llvm/ProfileData/InstrProf.h +++ include/llvm/ProfileData/InstrProf.h @@ -149,6 +149,7 @@ enum class instrprof_error { success = 0, eof, + unrecognized_format, bad_magic, bad_header, unsupported_version, Index: include/llvm/ProfileData/InstrProfReader.h =================================================================== --- include/llvm/ProfileData/InstrProfReader.h +++ include/llvm/ProfileData/InstrProfReader.h @@ -111,6 +111,9 @@ TextInstrProfReader(std::unique_ptr DataBuffer_) : DataBuffer(std::move(DataBuffer_)), Line(*DataBuffer, true, '#') {} + /// Return true if the given buffer is in text instrprof format. + static bool hasFormat(const MemoryBuffer &Buffer); + /// Read the header. std::error_code readHeader() override { return success(); } /// Read a single record. Index: include/llvm/ProfileData/SampleProfReader.h =================================================================== --- include/llvm/ProfileData/SampleProfReader.h +++ include/llvm/ProfileData/SampleProfReader.h @@ -292,6 +292,9 @@ /// \brief Read sample profiles from the associated file. std::error_code read() override; + + /// \brief Return true if \p Buffer is in the format supported by this class. + static bool hasFormat(const MemoryBuffer &Buffer); }; class SampleProfileReaderBinary : public SampleProfileReader { Index: lib/ProfileData/InstrProf.cpp =================================================================== --- lib/ProfileData/InstrProf.cpp +++ lib/ProfileData/InstrProf.cpp @@ -32,20 +32,22 @@ return "Success"; case instrprof_error::eof: return "End of File"; + case instrprof_error::unrecognized_format: + return "Unrecognized instrumentation profile encoding format"; case instrprof_error::bad_magic: - return "Invalid profile data (bad magic)"; + return "Invalid instrumentation profile data (bad magic)"; case instrprof_error::bad_header: - return "Invalid profile data (file header is corrupt)"; + return "Invalid instrumentation profile data (file header is corrupt)"; case instrprof_error::unsupported_version: - return "Unsupported profiling format version"; + return "Unsupported instrumentation profile format version"; case instrprof_error::unsupported_hash_type: - return "Unsupported profiling hash"; + return "Unsupported instrumentation profile hash type"; case instrprof_error::too_large: return "Too much profile data"; case instrprof_error::truncated: return "Truncated profile data"; case instrprof_error::malformed: - return "Malformed profile data"; + return "Malformed instrumentation profile data"; case instrprof_error::unknown_function: return "No profile data available for function"; case instrprof_error::hash_mismatch: Index: lib/ProfileData/InstrProfReader.cpp =================================================================== --- lib/ProfileData/InstrProfReader.cpp +++ lib/ProfileData/InstrProfReader.cpp @@ -54,8 +54,10 @@ Result.reset(new RawInstrProfReader64(std::move(Buffer))); else if (RawInstrProfReader32::hasFormat(*Buffer)) Result.reset(new RawInstrProfReader32(std::move(Buffer))); - else + else if (TextInstrProfReader::hasFormat(*Buffer)) Result.reset(new TextInstrProfReader(std::move(Buffer))); + else + return instrprof_error::unrecognized_format; // Initialize the reader and return the result. if (std::error_code EC = initializeReader(*Result)) @@ -97,6 +99,15 @@ *this = InstrProfIterator(); } +bool TextInstrProfReader::hasFormat(const MemoryBuffer &Buffer) { + // Verify that this really looks like plain ASCII text by checking a + // 'reasonable' number of characters (up to profile magic size). + size_t count = std::min(Buffer.getBufferSize(), sizeof(uint64_t)); + StringRef buffer = Buffer.getBufferStart(); + return count == 0 || std::all_of(buffer.begin(), buffer.begin() + count, + [](char c) { return ::isprint(c) || ::isspace(c); }); +} + std::error_code TextInstrProfReader::readNextRecord(InstrProfRecord &Record) { // Skip empty lines and comments. while (!Line.is_at_end() && (Line->empty() || Line->startswith("#"))) Index: lib/ProfileData/SampleProf.cpp =================================================================== --- lib/ProfileData/SampleProf.cpp +++ lib/ProfileData/SampleProf.cpp @@ -27,17 +27,17 @@ case sampleprof_error::success: return "Success"; case sampleprof_error::bad_magic: - return "Invalid file format (bad magic)"; + return "Invalid sample profile data (bad magic)"; case sampleprof_error::unsupported_version: - return "Unsupported format version"; + return "Unsupported sample profile format version"; case sampleprof_error::too_large: return "Too much profile data"; case sampleprof_error::truncated: return "Truncated profile data"; case sampleprof_error::malformed: - return "Malformed profile data"; + return "Malformed sample profile data"; case sampleprof_error::unrecognized_format: - return "Unrecognized profile encoding format"; + return "Unrecognized sample profile encoding format"; case sampleprof_error::unsupported_writing_format: return "Profile encoding format unsupported for writing operations"; case sampleprof_error::truncated_name_table: Index: lib/ProfileData/SampleProfReader.cpp =================================================================== --- lib/ProfileData/SampleProfReader.cpp +++ lib/ProfileData/SampleProfReader.cpp @@ -253,6 +253,22 @@ return sampleprof_error::success; } +bool SampleProfileReaderText::hasFormat(const MemoryBuffer &Buffer) { + bool result = false; + + // Check that the first non-comment line is a valid function header. + line_iterator LineIt(Buffer, /*SkipBlanks=*/true, '#'); + if (!LineIt.is_at_eof()) { + if ((*LineIt)[0] != ' ') { + uint64_t NumSamples, NumHeadSamples; + StringRef FName; + result = ParseHead(*LineIt, FName, NumSamples, NumHeadSamples); + } + } + + return result; +} + template ErrorOr SampleProfileReaderBinary::readNumber() { unsigned NumBytesRead = 0; std::error_code EC; @@ -716,8 +732,10 @@ Reader.reset(new SampleProfileReaderBinary(std::move(Buffer), C)); else if (SampleProfileReaderGCC::hasFormat(*Buffer)) Reader.reset(new SampleProfileReaderGCC(std::move(Buffer), C)); - else + else if (SampleProfileReaderText::hasFormat(*Buffer)) Reader.reset(new SampleProfileReaderText(std::move(Buffer), C)); + else + return sampleprof_error::unrecognized_format; if (std::error_code EC = Reader->readHeader()) return EC; Index: test/tools/llvm-profdata/raw-magic-but-no-header.test =================================================================== --- test/tools/llvm-profdata/raw-magic-but-no-header.test +++ test/tools/llvm-profdata/raw-magic-but-no-header.test @@ -3,4 +3,4 @@ RUN: printf '\377lprofr\201' > %t RUN: not llvm-profdata show %t 2>&1 | FileCheck %s -CHECK: error: {{.+}}: Invalid profile data (file header is corrupt) +CHECK: error: {{.+}}: Invalid instrumentation profile data (file header is corrupt) Index: test/tools/llvm-profdata/sample-profile-basic.test =================================================================== --- test/tools/llvm-profdata/sample-profile-basic.test +++ test/tools/llvm-profdata/sample-profile-basic.test @@ -28,3 +28,7 @@ MERGE1: main:368038:0 MERGE1: 9: 4128 _Z3fooi:1262 _Z3bari:2942 MERGE1: _Z3fooi:15422:1220 + +5- Detect invalid text encoding (e.g. instrumentation profile text format). +RUN: not llvm-profdata show --sample %p/Inputs/foo3bar3-1.proftext 2>&1 | FileCheck %s --check-prefix=BADTEXT +BADTEXT: error: {{.+}}: Unrecognized sample profile encoding format Index: test/tools/llvm-profdata/text-format-errors.test =================================================================== --- test/tools/llvm-profdata/text-format-errors.test +++ test/tools/llvm-profdata/text-format-errors.test @@ -1,10 +1,21 @@ +Tests for instrumentation profile bad encoding. + +1- Detect invalid count RUN: not llvm-profdata show %p/Inputs/invalid-count-later.proftext 2>&1 | FileCheck %s --check-prefix=INVALID-COUNT-LATER RUN: not llvm-profdata merge %p/Inputs/invalid-count-later.proftext %p/Inputs/invalid-count-later.profdata -o %t.out 2>&1 | FileCheck %s --check-prefix=INVALID-COUNT-LATER -INVALID-COUNT-LATER: error: {{.*}}invalid-count-later.proftext: Malformed profile data +INVALID-COUNT-LATER: error: {{.*}}invalid-count-later.proftext: Malformed instrumentation profile data +2- Detect bad hash RUN: not llvm-profdata show %p/Inputs/bad-hash.proftext 2>&1 | FileCheck %s --check-prefix=BAD-HASH RUN: not llvm-profdata merge %p/Inputs/bad-hash.proftext %p/Inputs/bad-hash.proftext -o %t.out 2>&1 | FileCheck %s --check-prefix=BAD-HASH -BAD-HASH: error: {{.*}}bad-hash.proftext: Malformed profile data +BAD-HASH: error: {{.*}}bad-hash.proftext: Malformed instrumentation profile data +3- Detect no counts RUN: not llvm-profdata show %p/Inputs/no-counts.proftext 2>&1 | FileCheck %s --check-prefix=NO-COUNTS -NO-COUNTS: error: {{.*}}no-counts.proftext: Malformed profile data +NO-COUNTS: error: {{.*}}no-counts.proftext: Malformed instrumentation profile data + +4- Detect binary input +RUN: echo -n $'\xff\xe5\xd0\xb1\xf4\c9\x94\xa8' > %t.bin +RUN: not llvm-profdata show %t.bin 2>&1 | FileCheck %s --check-prefix=BINARY +BINARY: error: {{.+}}: Unrecognized instrumentation profile encoding format +BINARY: Perhaps you forgot to use the -sample option? Index: tools/llvm-profdata/llvm-profdata.cpp =================================================================== --- tools/llvm-profdata/llvm-profdata.cpp +++ tools/llvm-profdata/llvm-profdata.cpp @@ -29,16 +29,32 @@ using namespace llvm; -static void exitWithError(const Twine &Message, StringRef Whence = "") { +static void exitWithError(const Twine &Message, + StringRef Whence = "", + StringRef Hint = "") { errs() << "error: "; if (!Whence.empty()) errs() << Whence << ": "; errs() << Message << "\n"; + if (!Hint.empty()) + errs() << Hint << "\n"; ::exit(1); } +static void exitWithErrorCode(const std::error_code &Error, StringRef Whence = "") { + if (Error.category() == instrprof_category()) { + instrprof_error instrError = static_cast(Error.value()); + if (instrError == instrprof_error::unrecognized_format) { + // Hint for common error of forgetting -sample for sample profiles. + exitWithError(Error.message(), Whence, + "Perhaps you forgot to use the -sample option?"); + } + } + exitWithError(Error.message(), Whence); +} + namespace { -enum ProfileKinds { instr, sample }; + enum ProfileKinds { instr, sample }; } static void mergeInstrProfile(const cl::list &Inputs, @@ -49,20 +65,20 @@ std::error_code EC; raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None); if (EC) - exitWithError(EC.message(), OutputFilename); + exitWithErrorCode(EC, OutputFilename); InstrProfWriter Writer; for (const auto &Filename : Inputs) { auto ReaderOrErr = InstrProfReader::create(Filename); if (std::error_code ec = ReaderOrErr.getError()) - exitWithError(ec.message(), Filename); + exitWithErrorCode(ec, Filename); auto Reader = std::move(ReaderOrErr.get()); for (auto &I : *Reader) if (std::error_code EC = Writer.addRecord(std::move(I))) errs() << Filename << ": " << I.Name << ": " << EC.message() << "\n"; if (Reader->hasError()) - exitWithError(Reader->getError().message(), Filename); + exitWithErrorCode(Reader->getError(), Filename); } Writer.write(Output); } @@ -73,7 +89,7 @@ using namespace sampleprof; auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat); if (std::error_code EC = WriterOrErr.getError()) - exitWithError(EC.message(), OutputFilename); + exitWithErrorCode(EC, OutputFilename); auto Writer = std::move(WriterOrErr.get()); StringMap ProfileMap; @@ -82,7 +98,7 @@ auto ReaderOrErr = SampleProfileReader::create(Filename, getGlobalContext()); if (std::error_code EC = ReaderOrErr.getError()) - exitWithError(EC.message(), Filename); + exitWithErrorCode(EC, Filename); // We need to keep the readers around until after all the files are // read so that we do not lose the function names stored in each @@ -91,7 +107,7 @@ Readers.push_back(std::move(ReaderOrErr.get())); const auto Reader = Readers.back().get(); if (std::error_code EC = Reader->read()) - exitWithError(EC.message(), Filename); + exitWithErrorCode(EC, Filename); StringMap &Profiles = Reader->getProfiles(); for (StringMap::iterator I = Profiles.begin(), @@ -143,7 +159,7 @@ std::string ShowFunction, raw_fd_ostream &OS) { auto ReaderOrErr = InstrProfReader::create(Filename); if (std::error_code EC = ReaderOrErr.getError()) - exitWithError(EC.message(), Filename); + exitWithErrorCode(EC, Filename); auto Reader = std::move(ReaderOrErr.get()); uint64_t MaxFunctionCount = 0, MaxBlockCount = 0; @@ -198,7 +214,7 @@ } } if (Reader->hasError()) - exitWithError(Reader->getError().message(), Filename); + exitWithErrorCode(Reader->getError(), Filename); if (ShowAllFunctions || !ShowFunction.empty()) OS << "Functions shown: " << ShownFunctions << "\n"; @@ -214,11 +230,11 @@ using namespace sampleprof; auto ReaderOrErr = SampleProfileReader::create(Filename, getGlobalContext()); if (std::error_code EC = ReaderOrErr.getError()) - exitWithError(EC.message(), Filename); + exitWithErrorCode(EC, Filename); auto Reader = std::move(ReaderOrErr.get()); if (std::error_code EC = Reader->read()) - exitWithError(EC.message(), Filename); + exitWithErrorCode(EC, Filename); if (ShowAllFunctions || ShowFunction.empty()) Reader->dump(OS); @@ -259,7 +275,7 @@ std::error_code EC; raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text); if (EC) - exitWithError(EC.message(), OutputFilename); + exitWithErrorCode(EC, OutputFilename); if (ShowAllFunctions && !ShowFunction.empty()) errs() << "warning: -function argument ignored: showing all functions\n";