forked from OSchip/llvm-project
InstrProf: Read raw binary profile in llvm-profdata
Read a raw binary profile that corresponds to a memory dump from the runtime profile. The test is a binary file generated from cfe/trunk/test/Profile/c-general.c with the new compiler-rt runtime and the matching text version of the input. It includes instructions on how to regenerate. <rdar://problem/15950346> llvm-svn: 204496
This commit is contained in:
parent
76e8731b09
commit
24b4b65339
|
@ -19,6 +19,7 @@
|
||||||
#include "llvm/ProfileData/InstrProf.h"
|
#include "llvm/ProfileData/InstrProf.h"
|
||||||
#include "llvm/Support/LineIterator.h"
|
#include "llvm/Support/LineIterator.h"
|
||||||
#include "llvm/Support/MemoryBuffer.h"
|
#include "llvm/Support/MemoryBuffer.h"
|
||||||
|
#include "llvm/Support/Endian.h"
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
||||||
|
@ -59,20 +60,25 @@ public:
|
||||||
InstrProfReader() : LastError(instrprof_error::success) {}
|
InstrProfReader() : LastError(instrprof_error::success) {}
|
||||||
virtual ~InstrProfReader() {}
|
virtual ~InstrProfReader() {}
|
||||||
|
|
||||||
|
/// Read the header. Required before reading first record.
|
||||||
|
virtual error_code readHeader() = 0;
|
||||||
/// Read a single record.
|
/// Read a single record.
|
||||||
virtual error_code readNextRecord(InstrProfRecord &Record) = 0;
|
virtual error_code readNextRecord(InstrProfRecord &Record) = 0;
|
||||||
/// Iterator over profile data.
|
/// Iterator over profile data.
|
||||||
InstrProfIterator begin() { return InstrProfIterator(this); }
|
InstrProfIterator begin() { return InstrProfIterator(this); }
|
||||||
InstrProfIterator end() { return InstrProfIterator(); }
|
InstrProfIterator end() { return InstrProfIterator(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
/// Set the current error_code and return same.
|
/// Set the current error_code and return same.
|
||||||
error_code error(error_code EC) {
|
error_code error(error_code EC) {
|
||||||
LastError = EC;
|
LastError = EC;
|
||||||
return EC;
|
return EC;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the current error code and return a successful one.
|
/// Clear the current error code and return a successful one.
|
||||||
error_code success() { return error(instrprof_error::success); }
|
error_code success() { return error(instrprof_error::success); }
|
||||||
|
|
||||||
|
public:
|
||||||
/// Return true if the reader has finished reading the profile data.
|
/// Return true if the reader has finished reading the profile data.
|
||||||
bool isEOF() { return LastError == instrprof_error::eof; }
|
bool isEOF() { return LastError == instrprof_error::eof; }
|
||||||
/// Return true if the reader encountered an error reading profiling data.
|
/// Return true if the reader encountered an error reading profiling data.
|
||||||
|
@ -110,10 +116,72 @@ public:
|
||||||
TextInstrProfReader(std::unique_ptr<MemoryBuffer> &DataBuffer_)
|
TextInstrProfReader(std::unique_ptr<MemoryBuffer> &DataBuffer_)
|
||||||
: DataBuffer(DataBuffer_.release()), Line(*DataBuffer, '#') {}
|
: DataBuffer(DataBuffer_.release()), Line(*DataBuffer, '#') {}
|
||||||
|
|
||||||
|
/// Read the header.
|
||||||
|
error_code readHeader() override { return success(); }
|
||||||
/// Read a single record.
|
/// Read a single record.
|
||||||
error_code readNextRecord(InstrProfRecord &Record) override;
|
error_code readNextRecord(InstrProfRecord &Record) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Reader for the raw instrprof binary format from runtime.
|
||||||
|
///
|
||||||
|
/// This format is a raw memory dump of the instrumentation-baed profiling data
|
||||||
|
/// from the runtime. It has no index.
|
||||||
|
class RawInstrProfReader : public InstrProfReader {
|
||||||
|
private:
|
||||||
|
/// The profile data file contents.
|
||||||
|
std::unique_ptr<MemoryBuffer> DataBuffer;
|
||||||
|
/// The current set of counter values.
|
||||||
|
std::vector<uint64_t> Counts;
|
||||||
|
struct ProfileData {
|
||||||
|
const uint32_t NameSize;
|
||||||
|
const uint32_t NumCounters;
|
||||||
|
const uint64_t FuncHash;
|
||||||
|
const uint64_t NamePtr;
|
||||||
|
const uint64_t CounterPtr;
|
||||||
|
};
|
||||||
|
struct RawHeader {
|
||||||
|
const uint64_t Magic;
|
||||||
|
const uint64_t Version;
|
||||||
|
const uint64_t DataSize;
|
||||||
|
const uint64_t CountersSize;
|
||||||
|
const uint64_t NamesSize;
|
||||||
|
const uint64_t CountersDelta;
|
||||||
|
const uint64_t NamesDelta;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ShouldSwapBytes;
|
||||||
|
uint64_t CountersDelta;
|
||||||
|
uint64_t NamesDelta;
|
||||||
|
const ProfileData *Data;
|
||||||
|
const ProfileData *DataEnd;
|
||||||
|
const uint64_t *CountersStart;
|
||||||
|
const char *NamesStart;
|
||||||
|
|
||||||
|
RawInstrProfReader(const TextInstrProfReader &) LLVM_DELETED_FUNCTION;
|
||||||
|
RawInstrProfReader &operator=(const TextInstrProfReader &)
|
||||||
|
LLVM_DELETED_FUNCTION;
|
||||||
|
public:
|
||||||
|
RawInstrProfReader(std::unique_ptr<MemoryBuffer> &DataBuffer);
|
||||||
|
|
||||||
|
error_code readHeader() override;
|
||||||
|
error_code readNextRecord(InstrProfRecord &Record) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
error_code readHeader(const RawHeader &Header);
|
||||||
|
template <class IntT>
|
||||||
|
IntT swap(IntT Int) const {
|
||||||
|
return ShouldSwapBytes ? sys::SwapByteOrder(Int) : Int;
|
||||||
|
}
|
||||||
|
const uint64_t *getCounter(uint64_t CounterPtr) const {
|
||||||
|
ptrdiff_t Offset = (swap(CounterPtr) - CountersDelta) / sizeof(uint64_t);
|
||||||
|
return CountersStart + Offset;
|
||||||
|
}
|
||||||
|
const char *getName(uint64_t NamePtr) const {
|
||||||
|
ptrdiff_t Offset = (swap(NamePtr) - NamesDelta) / sizeof(char);
|
||||||
|
return NamesStart + Offset;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // end namespace llvm
|
} // end namespace llvm
|
||||||
|
|
||||||
#endif // LLVM_PROFILEDATA_INSTRPROF_READER_H__
|
#endif // LLVM_PROFILEDATA_INSTRPROF_READER_H__
|
||||||
|
|
|
@ -14,12 +14,23 @@
|
||||||
|
|
||||||
#include "llvm/ProfileData/InstrProfReader.h"
|
#include "llvm/ProfileData/InstrProfReader.h"
|
||||||
#include "llvm/ProfileData/InstrProf.h"
|
#include "llvm/ProfileData/InstrProf.h"
|
||||||
#include "llvm/Support/Endian.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
|
static uint64_t getRawMagic() {
|
||||||
|
return
|
||||||
|
uint64_t('l') << 56 |
|
||||||
|
uint64_t('p') << 48 |
|
||||||
|
uint64_t('r') << 40 |
|
||||||
|
uint64_t('o') << 32 |
|
||||||
|
uint64_t('f') << 24 |
|
||||||
|
uint64_t('r') << 16 |
|
||||||
|
uint64_t('a') << 8 |
|
||||||
|
uint64_t('w');
|
||||||
|
}
|
||||||
|
|
||||||
error_code InstrProfReader::create(std::string Path,
|
error_code InstrProfReader::create(std::string Path,
|
||||||
std::unique_ptr<InstrProfReader> &Result) {
|
std::unique_ptr<InstrProfReader> &Result) {
|
||||||
std::unique_ptr<MemoryBuffer> Buffer;
|
std::unique_ptr<MemoryBuffer> Buffer;
|
||||||
|
@ -30,10 +41,19 @@ error_code InstrProfReader::create(std::string Path,
|
||||||
if (Buffer->getBufferSize() > std::numeric_limits<unsigned>::max())
|
if (Buffer->getBufferSize() > std::numeric_limits<unsigned>::max())
|
||||||
return instrprof_error::too_large;
|
return instrprof_error::too_large;
|
||||||
|
|
||||||
// FIXME: This needs to determine which format the file is and construct the
|
if (Buffer->getBufferSize() < sizeof(uint64_t)) {
|
||||||
// correct subclass.
|
|
||||||
Result.reset(new TextInstrProfReader(Buffer));
|
Result.reset(new TextInstrProfReader(Buffer));
|
||||||
|
Result->readHeader();
|
||||||
|
return instrprof_error::success;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Magic = *(uint64_t *)Buffer->getBufferStart();
|
||||||
|
uint64_t SwappedMagic = sys::SwapByteOrder(Magic);
|
||||||
|
if (Magic == getRawMagic() || SwappedMagic == getRawMagic())
|
||||||
|
Result.reset(new RawInstrProfReader(Buffer));
|
||||||
|
else
|
||||||
|
Result.reset(new TextInstrProfReader(Buffer));
|
||||||
|
Result->readHeader();
|
||||||
return instrprof_error::success;
|
return instrprof_error::success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,3 +102,85 @@ error_code TextInstrProfReader::readNextRecord(InstrProfRecord &Record) {
|
||||||
|
|
||||||
return success();
|
return success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t getRawVersion() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
namespace {
|
||||||
|
}
|
||||||
|
RawInstrProfReader::RawInstrProfReader(std::unique_ptr<MemoryBuffer> &DataBuffer)
|
||||||
|
: DataBuffer(DataBuffer.release()) { }
|
||||||
|
|
||||||
|
error_code RawInstrProfReader::readHeader() {
|
||||||
|
if (DataBuffer->getBufferSize() < sizeof(RawHeader))
|
||||||
|
return error(instrprof_error::malformed);
|
||||||
|
const RawHeader *Header = (RawHeader *)DataBuffer->getBufferStart();
|
||||||
|
if (Header->Magic == getRawMagic())
|
||||||
|
ShouldSwapBytes = false;
|
||||||
|
else {
|
||||||
|
if (sys::SwapByteOrder(Header->Magic) != getRawMagic())
|
||||||
|
return error(instrprof_error::malformed);
|
||||||
|
|
||||||
|
ShouldSwapBytes = true;
|
||||||
|
}
|
||||||
|
return readHeader(*Header);
|
||||||
|
}
|
||||||
|
|
||||||
|
error_code RawInstrProfReader::readHeader(const RawHeader &Header) {
|
||||||
|
if (swap(Header.Version) != getRawVersion())
|
||||||
|
return error(instrprof_error::unsupported_version);
|
||||||
|
|
||||||
|
CountersDelta = swap(Header.CountersDelta);
|
||||||
|
NamesDelta = swap(Header.NamesDelta);
|
||||||
|
auto DataSize = swap(Header.DataSize);
|
||||||
|
auto CountersSize = swap(Header.CountersSize);
|
||||||
|
auto NamesSize = swap(Header.NamesSize);
|
||||||
|
|
||||||
|
ptrdiff_t DataOffset = sizeof(RawHeader);
|
||||||
|
ptrdiff_t CountersOffset = DataOffset + sizeof(ProfileData) * DataSize;
|
||||||
|
ptrdiff_t NamesOffset = CountersOffset + sizeof(uint64_t) * CountersSize;
|
||||||
|
size_t FileSize = NamesOffset + sizeof(char) * NamesSize;
|
||||||
|
|
||||||
|
if (FileSize != DataBuffer->getBufferSize())
|
||||||
|
return error(instrprof_error::malformed);
|
||||||
|
|
||||||
|
Data = (ProfileData *)(DataBuffer->getBufferStart() + DataOffset);
|
||||||
|
DataEnd = Data + DataSize;
|
||||||
|
CountersStart = (uint64_t *)(DataBuffer->getBufferStart() + CountersOffset);
|
||||||
|
NamesStart = DataBuffer->getBufferStart() + NamesOffset;
|
||||||
|
|
||||||
|
return success();
|
||||||
|
}
|
||||||
|
|
||||||
|
error_code RawInstrProfReader::readNextRecord(InstrProfRecord &Record) {
|
||||||
|
if (Data == DataEnd)
|
||||||
|
return error(instrprof_error::eof);
|
||||||
|
|
||||||
|
// Get the raw data.
|
||||||
|
StringRef RawName(getName(Data->NamePtr), swap(Data->NameSize));
|
||||||
|
auto RawCounts = makeArrayRef(getCounter(Data->CounterPtr),
|
||||||
|
swap(Data->NumCounters));
|
||||||
|
|
||||||
|
// Check bounds.
|
||||||
|
if (RawName.data() < NamesStart ||
|
||||||
|
RawName.data() + RawName.size() > DataBuffer->getBufferEnd() ||
|
||||||
|
RawCounts.data() < CountersStart ||
|
||||||
|
RawCounts.data() + RawCounts.size() > (uint64_t *)NamesStart)
|
||||||
|
return error(instrprof_error::malformed);
|
||||||
|
|
||||||
|
// Store the data in Record, byte-swapping as necessary.
|
||||||
|
Record.Hash = swap(Data->FuncHash);
|
||||||
|
Record.Name = RawName;
|
||||||
|
if (ShouldSwapBytes) {
|
||||||
|
Counts.clear();
|
||||||
|
Counts.reserve(RawCounts.size());
|
||||||
|
for (uint64_t Count : RawCounts)
|
||||||
|
Counts.push_back(swap(Count));
|
||||||
|
Record.Counts = Counts;
|
||||||
|
} else
|
||||||
|
Record.Counts = RawCounts;
|
||||||
|
|
||||||
|
// Iterate.
|
||||||
|
++Data;
|
||||||
|
return success();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
simple_loops
|
||||||
|
4
|
||||||
|
4
|
||||||
|
1
|
||||||
|
100
|
||||||
|
100
|
||||||
|
75
|
||||||
|
|
||||||
|
conditionals
|
||||||
|
11
|
||||||
|
11
|
||||||
|
1
|
||||||
|
100
|
||||||
|
50
|
||||||
|
50
|
||||||
|
33
|
||||||
|
33
|
||||||
|
16
|
||||||
|
99
|
||||||
|
100
|
||||||
|
99
|
||||||
|
100
|
||||||
|
|
||||||
|
early_exits
|
||||||
|
9
|
||||||
|
9
|
||||||
|
1
|
||||||
|
0
|
||||||
|
51
|
||||||
|
1
|
||||||
|
25
|
||||||
|
1
|
||||||
|
25
|
||||||
|
1
|
||||||
|
0
|
||||||
|
|
||||||
|
jumps
|
||||||
|
22
|
||||||
|
22
|
||||||
|
1
|
||||||
|
1
|
||||||
|
0
|
||||||
|
1
|
||||||
|
0
|
||||||
|
0
|
||||||
|
1
|
||||||
|
0
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
2
|
||||||
|
0
|
||||||
|
3
|
||||||
|
0
|
||||||
|
1
|
||||||
|
1
|
||||||
|
1
|
||||||
|
10
|
||||||
|
0
|
||||||
|
10
|
||||||
|
9
|
||||||
|
|
||||||
|
switches
|
||||||
|
19
|
||||||
|
19
|
||||||
|
1
|
||||||
|
1
|
||||||
|
1
|
||||||
|
15
|
||||||
|
7
|
||||||
|
1
|
||||||
|
0
|
||||||
|
2
|
||||||
|
2
|
||||||
|
3
|
||||||
|
3
|
||||||
|
4
|
||||||
|
4
|
||||||
|
0
|
||||||
|
4
|
||||||
|
4
|
||||||
|
5
|
||||||
|
1
|
||||||
|
0
|
||||||
|
|
||||||
|
big_switch
|
||||||
|
17
|
||||||
|
17
|
||||||
|
1
|
||||||
|
32
|
||||||
|
32
|
||||||
|
1
|
||||||
|
0
|
||||||
|
1
|
||||||
|
1
|
||||||
|
11
|
||||||
|
11
|
||||||
|
1
|
||||||
|
1
|
||||||
|
15
|
||||||
|
15
|
||||||
|
1
|
||||||
|
1
|
||||||
|
2
|
||||||
|
2
|
||||||
|
|
||||||
|
boolean_operators
|
||||||
|
8
|
||||||
|
8
|
||||||
|
1
|
||||||
|
100
|
||||||
|
34
|
||||||
|
66
|
||||||
|
17
|
||||||
|
34
|
||||||
|
33
|
||||||
|
50
|
||||||
|
|
||||||
|
boolop_loops
|
||||||
|
9
|
||||||
|
9
|
||||||
|
1
|
||||||
|
50
|
||||||
|
51
|
||||||
|
50
|
||||||
|
26
|
||||||
|
50
|
||||||
|
51
|
||||||
|
50
|
||||||
|
26
|
||||||
|
|
||||||
|
do_fallthrough
|
||||||
|
4
|
||||||
|
4
|
||||||
|
1
|
||||||
|
10
|
||||||
|
2
|
||||||
|
8
|
||||||
|
|
||||||
|
main
|
||||||
|
1
|
||||||
|
1
|
||||||
|
1
|
||||||
|
|
||||||
|
c-general.c:static_func
|
||||||
|
2
|
||||||
|
2
|
||||||
|
1
|
||||||
|
10
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,15 @@
|
||||||
|
REGENERATE: You need a checkout of clang with compiler-rt to generate the
|
||||||
|
REGENERATE: binary file here. These shell commands can be used to regenerate
|
||||||
|
REGENERATE: it.
|
||||||
|
REGENERATE:
|
||||||
|
REGENERATE: $ SRC=path/to/llvm
|
||||||
|
REGENERATE: $ CFE=$SRC/tools/clang
|
||||||
|
REGENERATE: $ TESTDIR=$SRC/test/tools/llvm-profdata
|
||||||
|
REGENERATE: $ CFE_TESTDIR=$CFE/test/Profile
|
||||||
|
REGENERATE: $ clang -o a.out -fprofile-instr-generate $CFE_TESTDIR/test/Profile/c-general.c
|
||||||
|
REGENERATE: $ LLVM_PROFILE_FILE=$TESTDIR/Inputs/binary.profdata ./a.out
|
||||||
|
REGENERATE: $ cp $CFE_TESTDIR/Inputs/c-general.profdata $TESTDIR/Inputs/binary-compare.profdata
|
||||||
|
|
||||||
|
RUN: llvm-profdata show %p/Inputs/binary.profdata -o %t1
|
||||||
|
RUN: llvm-profdata show %p/Inputs/binary-compare.profdata -o %t2
|
||||||
|
RUN: diff -up %t1 %t2
|
Loading…
Reference in New Issue