Skip to content

Commit

Permalink
InstrProf: Read raw binary profile in llvm-profdata
Browse files Browse the repository at this point in the history
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>

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@204496 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
dexonsmith committed Mar 21, 2014
1 parent c61e1f5 commit ddfcb21
Show file tree
Hide file tree
Showing 5 changed files with 339 additions and 4 deletions.
68 changes: 68 additions & 0 deletions include/llvm/ProfileData/InstrProfReader.h
Expand Up @@ -19,6 +19,7 @@
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Endian.h"

#include <iterator>

Expand Down Expand Up @@ -59,20 +60,25 @@ class InstrProfReader {
InstrProfReader() : LastError(instrprof_error::success) {}
virtual ~InstrProfReader() {}

/// Read the header. Required before reading first record.
virtual error_code readHeader() = 0;
/// Read a single record.
virtual error_code readNextRecord(InstrProfRecord &Record) = 0;
/// Iterator over profile data.
InstrProfIterator begin() { return InstrProfIterator(this); }
InstrProfIterator end() { return InstrProfIterator(); }

protected:
/// Set the current error_code and return same.
error_code error(error_code EC) {
LastError = EC;
return EC;
}

/// Clear the current error code and return a successful one.
error_code success() { return error(instrprof_error::success); }

public:
/// Return true if the reader has finished reading the profile data.
bool isEOF() { return LastError == instrprof_error::eof; }
/// Return true if the reader encountered an error reading profiling data.
Expand Down Expand Up @@ -110,10 +116,72 @@ class TextInstrProfReader : public InstrProfReader {
TextInstrProfReader(std::unique_ptr<MemoryBuffer> &DataBuffer_)
: DataBuffer(DataBuffer_.release()), Line(*DataBuffer, '#') {}

/// Read the header.
error_code readHeader() override { return success(); }
/// Read a single record.
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

#endif // LLVM_PROFILEDATA_INSTRPROF_READER_H__
110 changes: 106 additions & 4 deletions lib/ProfileData/InstrProfReader.cpp
Expand Up @@ -14,12 +14,23 @@

#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Endian.h"

#include <cassert>

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,
std::unique_ptr<InstrProfReader> &Result) {
std::unique_ptr<MemoryBuffer> Buffer;
Expand All @@ -30,10 +41,19 @@ error_code InstrProfReader::create(std::string Path,
if (Buffer->getBufferSize() > std::numeric_limits<unsigned>::max())
return instrprof_error::too_large;

// FIXME: This needs to determine which format the file is and construct the
// correct subclass.
Result.reset(new TextInstrProfReader(Buffer));
if (Buffer->getBufferSize() < sizeof(uint64_t)) {
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;
}

Expand Down Expand Up @@ -82,3 +102,85 @@ error_code TextInstrProfReader::readNextRecord(InstrProfRecord &Record) {

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();
}
150 changes: 150 additions & 0 deletions test/tools/llvm-profdata/Inputs/binary-compare.profdata
@@ -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 added test/tools/llvm-profdata/Inputs/binary.profdata
Binary file not shown.

0 comments on commit ddfcb21

Please sign in to comment.