You've already forked linux-packaging-mono
Imported Upstream version 5.18.0.205
Former-commit-id: 7f59f7e792705db773f1caecdaa823092f4e2927
This commit is contained in:
parent
5cd5df71cc
commit
8e12397d70
18
external/llvm/lib/ProfileData/CMakeLists.txt
vendored
Normal file
18
external/llvm/lib/ProfileData/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
add_llvm_library(LLVMProfileData
|
||||
GCOV.cpp
|
||||
InstrProf.cpp
|
||||
InstrProfReader.cpp
|
||||
InstrProfWriter.cpp
|
||||
ProfileSummaryBuilder.cpp
|
||||
SampleProf.cpp
|
||||
SampleProfReader.cpp
|
||||
SampleProfWriter.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${LLVM_MAIN_INCLUDE_DIR}/llvm/ProfileData
|
||||
|
||||
DEPENDS
|
||||
intrinsics_gen
|
||||
)
|
||||
|
||||
add_subdirectory(Coverage)
|
11
external/llvm/lib/ProfileData/Coverage/CMakeLists.txt
vendored
Normal file
11
external/llvm/lib/ProfileData/Coverage/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
add_llvm_library(LLVMCoverage
|
||||
CoverageMapping.cpp
|
||||
CoverageMappingWriter.cpp
|
||||
CoverageMappingReader.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${LLVM_MAIN_INCLUDE_DIR}/llvm/ProfileData/Coverage
|
||||
|
||||
DEPENDS
|
||||
intrinsics_gen
|
||||
)
|
775
external/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
vendored
Normal file
775
external/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
750
external/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
vendored
Normal file
750
external/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
208
external/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp
vendored
Normal file
208
external/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp
vendored
Normal file
@ -0,0 +1,208 @@
|
||||
//===- CoverageMappingWriter.cpp - Code coverage mapping writer -----------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains support for writing coverage mapping data for
|
||||
// instrumentation based coverage.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/Support/LEB128.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace coverage;
|
||||
|
||||
void CoverageFilenamesSectionWriter::write(raw_ostream &OS) {
|
||||
encodeULEB128(Filenames.size(), OS);
|
||||
for (const auto &Filename : Filenames) {
|
||||
encodeULEB128(Filename.size(), OS);
|
||||
OS << Filename;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/// \brief Gather only the expressions that are used by the mapping
|
||||
/// regions in this function.
|
||||
class CounterExpressionsMinimizer {
|
||||
ArrayRef<CounterExpression> Expressions;
|
||||
SmallVector<CounterExpression, 16> UsedExpressions;
|
||||
std::vector<unsigned> AdjustedExpressionIDs;
|
||||
|
||||
public:
|
||||
CounterExpressionsMinimizer(ArrayRef<CounterExpression> Expressions,
|
||||
ArrayRef<CounterMappingRegion> MappingRegions)
|
||||
: Expressions(Expressions) {
|
||||
AdjustedExpressionIDs.resize(Expressions.size(), 0);
|
||||
for (const auto &I : MappingRegions)
|
||||
mark(I.Count);
|
||||
for (const auto &I : MappingRegions)
|
||||
gatherUsed(I.Count);
|
||||
}
|
||||
|
||||
void mark(Counter C) {
|
||||
if (!C.isExpression())
|
||||
return;
|
||||
unsigned ID = C.getExpressionID();
|
||||
AdjustedExpressionIDs[ID] = 1;
|
||||
mark(Expressions[ID].LHS);
|
||||
mark(Expressions[ID].RHS);
|
||||
}
|
||||
|
||||
void gatherUsed(Counter C) {
|
||||
if (!C.isExpression() || !AdjustedExpressionIDs[C.getExpressionID()])
|
||||
return;
|
||||
AdjustedExpressionIDs[C.getExpressionID()] = UsedExpressions.size();
|
||||
const auto &E = Expressions[C.getExpressionID()];
|
||||
UsedExpressions.push_back(E);
|
||||
gatherUsed(E.LHS);
|
||||
gatherUsed(E.RHS);
|
||||
}
|
||||
|
||||
ArrayRef<CounterExpression> getExpressions() const { return UsedExpressions; }
|
||||
|
||||
/// \brief Adjust the given counter to correctly transition from the old
|
||||
/// expression ids to the new expression ids.
|
||||
Counter adjust(Counter C) const {
|
||||
if (C.isExpression())
|
||||
C = Counter::getExpression(AdjustedExpressionIDs[C.getExpressionID()]);
|
||||
return C;
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
/// \brief Encode the counter.
|
||||
///
|
||||
/// The encoding uses the following format:
|
||||
/// Low 2 bits - Tag:
|
||||
/// Counter::Zero(0) - A Counter with kind Counter::Zero
|
||||
/// Counter::CounterValueReference(1) - A counter with kind
|
||||
/// Counter::CounterValueReference
|
||||
/// Counter::Expression(2) + CounterExpression::Subtract(0) -
|
||||
/// A counter with kind Counter::Expression and an expression
|
||||
/// with kind CounterExpression::Subtract
|
||||
/// Counter::Expression(2) + CounterExpression::Add(1) -
|
||||
/// A counter with kind Counter::Expression and an expression
|
||||
/// with kind CounterExpression::Add
|
||||
/// Remaining bits - Counter/Expression ID.
|
||||
static unsigned encodeCounter(ArrayRef<CounterExpression> Expressions,
|
||||
Counter C) {
|
||||
unsigned Tag = unsigned(C.getKind());
|
||||
if (C.isExpression())
|
||||
Tag += Expressions[C.getExpressionID()].Kind;
|
||||
unsigned ID = C.getCounterID();
|
||||
assert(ID <=
|
||||
(std::numeric_limits<unsigned>::max() >> Counter::EncodingTagBits));
|
||||
return Tag | (ID << Counter::EncodingTagBits);
|
||||
}
|
||||
|
||||
static void writeCounter(ArrayRef<CounterExpression> Expressions, Counter C,
|
||||
raw_ostream &OS) {
|
||||
encodeULEB128(encodeCounter(Expressions, C), OS);
|
||||
}
|
||||
|
||||
void CoverageMappingWriter::write(raw_ostream &OS) {
|
||||
// Check that we don't have any bogus regions.
|
||||
assert(all_of(MappingRegions,
|
||||
[](const CounterMappingRegion &CMR) {
|
||||
return CMR.startLoc() <= CMR.endLoc();
|
||||
}) &&
|
||||
"Source region does not begin before it ends");
|
||||
|
||||
// Sort the regions in an ascending order by the file id and the starting
|
||||
// location. Sort by region kinds to ensure stable order for tests.
|
||||
std::stable_sort(
|
||||
MappingRegions.begin(), MappingRegions.end(),
|
||||
[](const CounterMappingRegion &LHS, const CounterMappingRegion &RHS) {
|
||||
if (LHS.FileID != RHS.FileID)
|
||||
return LHS.FileID < RHS.FileID;
|
||||
if (LHS.startLoc() != RHS.startLoc())
|
||||
return LHS.startLoc() < RHS.startLoc();
|
||||
return LHS.Kind < RHS.Kind;
|
||||
});
|
||||
|
||||
// Write out the fileid -> filename mapping.
|
||||
encodeULEB128(VirtualFileMapping.size(), OS);
|
||||
for (const auto &FileID : VirtualFileMapping)
|
||||
encodeULEB128(FileID, OS);
|
||||
|
||||
// Write out the expressions.
|
||||
CounterExpressionsMinimizer Minimizer(Expressions, MappingRegions);
|
||||
auto MinExpressions = Minimizer.getExpressions();
|
||||
encodeULEB128(MinExpressions.size(), OS);
|
||||
for (const auto &E : MinExpressions) {
|
||||
writeCounter(MinExpressions, Minimizer.adjust(E.LHS), OS);
|
||||
writeCounter(MinExpressions, Minimizer.adjust(E.RHS), OS);
|
||||
}
|
||||
|
||||
// Write out the mapping regions.
|
||||
// Split the regions into subarrays where each region in a
|
||||
// subarray has a fileID which is the index of that subarray.
|
||||
unsigned PrevLineStart = 0;
|
||||
unsigned CurrentFileID = ~0U;
|
||||
for (auto I = MappingRegions.begin(), E = MappingRegions.end(); I != E; ++I) {
|
||||
if (I->FileID != CurrentFileID) {
|
||||
// Ensure that all file ids have at least one mapping region.
|
||||
assert(I->FileID == (CurrentFileID + 1));
|
||||
// Find the number of regions with this file id.
|
||||
unsigned RegionCount = 1;
|
||||
for (auto J = I + 1; J != E && I->FileID == J->FileID; ++J)
|
||||
++RegionCount;
|
||||
// Start a new region sub-array.
|
||||
encodeULEB128(RegionCount, OS);
|
||||
|
||||
CurrentFileID = I->FileID;
|
||||
PrevLineStart = 0;
|
||||
}
|
||||
Counter Count = Minimizer.adjust(I->Count);
|
||||
switch (I->Kind) {
|
||||
case CounterMappingRegion::CodeRegion:
|
||||
case CounterMappingRegion::GapRegion:
|
||||
writeCounter(MinExpressions, Count, OS);
|
||||
break;
|
||||
case CounterMappingRegion::ExpansionRegion: {
|
||||
assert(Count.isZero());
|
||||
assert(I->ExpandedFileID <=
|
||||
(std::numeric_limits<unsigned>::max() >>
|
||||
Counter::EncodingCounterTagAndExpansionRegionTagBits));
|
||||
// Mark an expansion region with a set bit that follows the counter tag,
|
||||
// and pack the expanded file id into the remaining bits.
|
||||
unsigned EncodedTagExpandedFileID =
|
||||
(1 << Counter::EncodingTagBits) |
|
||||
(I->ExpandedFileID
|
||||
<< Counter::EncodingCounterTagAndExpansionRegionTagBits);
|
||||
encodeULEB128(EncodedTagExpandedFileID, OS);
|
||||
break;
|
||||
}
|
||||
case CounterMappingRegion::SkippedRegion:
|
||||
assert(Count.isZero());
|
||||
encodeULEB128(unsigned(I->Kind)
|
||||
<< Counter::EncodingCounterTagAndExpansionRegionTagBits,
|
||||
OS);
|
||||
break;
|
||||
}
|
||||
assert(I->LineStart >= PrevLineStart);
|
||||
encodeULEB128(I->LineStart - PrevLineStart, OS);
|
||||
encodeULEB128(I->ColumnStart, OS);
|
||||
assert(I->LineEnd >= I->LineStart);
|
||||
encodeULEB128(I->LineEnd - I->LineStart, OS);
|
||||
encodeULEB128(I->ColumnEnd, OS);
|
||||
PrevLineStart = I->LineStart;
|
||||
}
|
||||
// Ensure that all file ids have at least one mapping region.
|
||||
assert(CurrentFileID == (VirtualFileMapping.size() - 1));
|
||||
}
|
23
external/llvm/lib/ProfileData/Coverage/LLVMBuild.txt
vendored
Normal file
23
external/llvm/lib/ProfileData/Coverage/LLVMBuild.txt
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
;===- ./lib/ProfileData/Coverage/LLVMBuild.txt -----------------*- Conf -*--===;
|
||||
;
|
||||
; The LLVM Compiler Infrastructure
|
||||
;
|
||||
; This file is distributed under the University of Illinois Open Source
|
||||
; License. See LICENSE.TXT for details.
|
||||
;
|
||||
;===------------------------------------------------------------------------===;
|
||||
;
|
||||
; This is an LLVMBuild description file for the components in this subdirectory.
|
||||
;
|
||||
; For more information on the LLVMBuild system, please see:
|
||||
;
|
||||
; http://llvm.org/docs/LLVMBuild.html
|
||||
;
|
||||
;===------------------------------------------------------------------------===;
|
||||
|
||||
[component_0]
|
||||
type = Library
|
||||
name = Coverage
|
||||
parent = ProfileData
|
||||
required_libraries = Core Object ProfileData Support
|
||||
|
821
external/llvm/lib/ProfileData/GCOV.cpp
vendored
Normal file
821
external/llvm/lib/ProfileData/GCOV.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1014
external/llvm/lib/ProfileData/InstrProf.cpp
vendored
Normal file
1014
external/llvm/lib/ProfileData/InstrProf.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
748
external/llvm/lib/ProfileData/InstrProfReader.cpp
vendored
Normal file
748
external/llvm/lib/ProfileData/InstrProfReader.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
389
external/llvm/lib/ProfileData/InstrProfWriter.cpp
vendored
Normal file
389
external/llvm/lib/ProfileData/InstrProfWriter.cpp
vendored
Normal file
@ -0,0 +1,389 @@
|
||||
//===- InstrProfWriter.cpp - Instrumented profiling writer ----------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains support for writing profiling data for clang's
|
||||
// instrumentation based PGO and coverage.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ProfileData/InstrProfWriter.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/IR/ProfileSummary.h"
|
||||
#include "llvm/ProfileData/InstrProf.h"
|
||||
#include "llvm/ProfileData/ProfileCommon.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/EndianStream.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/OnDiskHashTable.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
// A struct to define how the data stream should be patched. For Indexed
|
||||
// profiling, only uint64_t data type is needed.
|
||||
struct PatchItem {
|
||||
uint64_t Pos; // Where to patch.
|
||||
uint64_t *D; // Pointer to an array of source data.
|
||||
int N; // Number of elements in \c D array.
|
||||
};
|
||||
|
||||
namespace llvm {
|
||||
|
||||
// A wrapper class to abstract writer stream with support of bytes
|
||||
// back patching.
|
||||
class ProfOStream {
|
||||
public:
|
||||
ProfOStream(raw_fd_ostream &FD) : IsFDOStream(true), OS(FD), LE(FD) {}
|
||||
ProfOStream(raw_string_ostream &STR)
|
||||
: IsFDOStream(false), OS(STR), LE(STR) {}
|
||||
|
||||
uint64_t tell() { return OS.tell(); }
|
||||
void write(uint64_t V) { LE.write<uint64_t>(V); }
|
||||
|
||||
// \c patch can only be called when all data is written and flushed.
|
||||
// For raw_string_ostream, the patch is done on the target string
|
||||
// directly and it won't be reflected in the stream's internal buffer.
|
||||
void patch(PatchItem *P, int NItems) {
|
||||
using namespace support;
|
||||
|
||||
if (IsFDOStream) {
|
||||
raw_fd_ostream &FDOStream = static_cast<raw_fd_ostream &>(OS);
|
||||
for (int K = 0; K < NItems; K++) {
|
||||
FDOStream.seek(P[K].Pos);
|
||||
for (int I = 0; I < P[K].N; I++)
|
||||
write(P[K].D[I]);
|
||||
}
|
||||
} else {
|
||||
raw_string_ostream &SOStream = static_cast<raw_string_ostream &>(OS);
|
||||
std::string &Data = SOStream.str(); // with flush
|
||||
for (int K = 0; K < NItems; K++) {
|
||||
for (int I = 0; I < P[K].N; I++) {
|
||||
uint64_t Bytes = endian::byte_swap<uint64_t, little>(P[K].D[I]);
|
||||
Data.replace(P[K].Pos + I * sizeof(uint64_t), sizeof(uint64_t),
|
||||
(const char *)&Bytes, sizeof(uint64_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If \c OS is an instance of \c raw_fd_ostream, this field will be
|
||||
// true. Otherwise, \c OS will be an raw_string_ostream.
|
||||
bool IsFDOStream;
|
||||
raw_ostream &OS;
|
||||
support::endian::Writer<support::little> LE;
|
||||
};
|
||||
|
||||
class InstrProfRecordWriterTrait {
|
||||
public:
|
||||
using key_type = StringRef;
|
||||
using key_type_ref = StringRef;
|
||||
|
||||
using data_type = const InstrProfWriter::ProfilingData *const;
|
||||
using data_type_ref = const InstrProfWriter::ProfilingData *const;
|
||||
|
||||
using hash_value_type = uint64_t;
|
||||
using offset_type = uint64_t;
|
||||
|
||||
support::endianness ValueProfDataEndianness = support::little;
|
||||
InstrProfSummaryBuilder *SummaryBuilder;
|
||||
|
||||
InstrProfRecordWriterTrait() = default;
|
||||
|
||||
static hash_value_type ComputeHash(key_type_ref K) {
|
||||
return IndexedInstrProf::ComputeHash(K);
|
||||
}
|
||||
|
||||
static std::pair<offset_type, offset_type>
|
||||
EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
|
||||
using namespace support;
|
||||
|
||||
endian::Writer<little> LE(Out);
|
||||
|
||||
offset_type N = K.size();
|
||||
LE.write<offset_type>(N);
|
||||
|
||||
offset_type M = 0;
|
||||
for (const auto &ProfileData : *V) {
|
||||
const InstrProfRecord &ProfRecord = ProfileData.second;
|
||||
M += sizeof(uint64_t); // The function hash
|
||||
M += sizeof(uint64_t); // The size of the Counts vector
|
||||
M += ProfRecord.Counts.size() * sizeof(uint64_t);
|
||||
|
||||
// Value data
|
||||
M += ValueProfData::getSize(ProfileData.second);
|
||||
}
|
||||
LE.write<offset_type>(M);
|
||||
|
||||
return std::make_pair(N, M);
|
||||
}
|
||||
|
||||
void EmitKey(raw_ostream &Out, key_type_ref K, offset_type N) {
|
||||
Out.write(K.data(), N);
|
||||
}
|
||||
|
||||
void EmitData(raw_ostream &Out, key_type_ref, data_type_ref V, offset_type) {
|
||||
using namespace support;
|
||||
|
||||
endian::Writer<little> LE(Out);
|
||||
for (const auto &ProfileData : *V) {
|
||||
const InstrProfRecord &ProfRecord = ProfileData.second;
|
||||
SummaryBuilder->addRecord(ProfRecord);
|
||||
|
||||
LE.write<uint64_t>(ProfileData.first); // Function hash
|
||||
LE.write<uint64_t>(ProfRecord.Counts.size());
|
||||
for (uint64_t I : ProfRecord.Counts)
|
||||
LE.write<uint64_t>(I);
|
||||
|
||||
// Write value data
|
||||
std::unique_ptr<ValueProfData> VDataPtr =
|
||||
ValueProfData::serializeFrom(ProfileData.second);
|
||||
uint32_t S = VDataPtr->getSize();
|
||||
VDataPtr->swapBytesFromHost(ValueProfDataEndianness);
|
||||
Out.write((const char *)VDataPtr.get(), S);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
InstrProfWriter::InstrProfWriter(bool Sparse)
|
||||
: Sparse(Sparse), InfoObj(new InstrProfRecordWriterTrait()) {}
|
||||
|
||||
InstrProfWriter::~InstrProfWriter() { delete InfoObj; }
|
||||
|
||||
// Internal interface for testing purpose only.
|
||||
void InstrProfWriter::setValueProfDataEndianness(
|
||||
support::endianness Endianness) {
|
||||
InfoObj->ValueProfDataEndianness = Endianness;
|
||||
}
|
||||
|
||||
void InstrProfWriter::setOutputSparse(bool Sparse) {
|
||||
this->Sparse = Sparse;
|
||||
}
|
||||
|
||||
void InstrProfWriter::addRecord(NamedInstrProfRecord &&I, uint64_t Weight,
|
||||
function_ref<void(Error)> Warn) {
|
||||
auto Name = I.Name;
|
||||
auto Hash = I.Hash;
|
||||
addRecord(Name, Hash, std::move(I), Weight, Warn);
|
||||
}
|
||||
|
||||
void InstrProfWriter::addRecord(StringRef Name, uint64_t Hash,
|
||||
InstrProfRecord &&I, uint64_t Weight,
|
||||
function_ref<void(Error)> Warn) {
|
||||
auto &ProfileDataMap = FunctionData[Name];
|
||||
|
||||
bool NewFunc;
|
||||
ProfilingData::iterator Where;
|
||||
std::tie(Where, NewFunc) =
|
||||
ProfileDataMap.insert(std::make_pair(Hash, InstrProfRecord()));
|
||||
InstrProfRecord &Dest = Where->second;
|
||||
|
||||
auto MapWarn = [&](instrprof_error E) {
|
||||
Warn(make_error<InstrProfError>(E));
|
||||
};
|
||||
|
||||
if (NewFunc) {
|
||||
// We've never seen a function with this name and hash, add it.
|
||||
Dest = std::move(I);
|
||||
if (Weight > 1)
|
||||
Dest.scale(Weight, MapWarn);
|
||||
} else {
|
||||
// We're updating a function we've seen before.
|
||||
Dest.merge(I, Weight, MapWarn);
|
||||
}
|
||||
|
||||
Dest.sortValueData();
|
||||
}
|
||||
|
||||
void InstrProfWriter::mergeRecordsFromWriter(InstrProfWriter &&IPW,
|
||||
function_ref<void(Error)> Warn) {
|
||||
for (auto &I : IPW.FunctionData)
|
||||
for (auto &Func : I.getValue())
|
||||
addRecord(I.getKey(), Func.first, std::move(Func.second), 1, Warn);
|
||||
}
|
||||
|
||||
bool InstrProfWriter::shouldEncodeData(const ProfilingData &PD) {
|
||||
if (!Sparse)
|
||||
return true;
|
||||
for (const auto &Func : PD) {
|
||||
const InstrProfRecord &IPR = Func.second;
|
||||
if (llvm::any_of(IPR.Counts, [](uint64_t Count) { return Count > 0; }))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void setSummary(IndexedInstrProf::Summary *TheSummary,
|
||||
ProfileSummary &PS) {
|
||||
using namespace IndexedInstrProf;
|
||||
|
||||
std::vector<ProfileSummaryEntry> &Res = PS.getDetailedSummary();
|
||||
TheSummary->NumSummaryFields = Summary::NumKinds;
|
||||
TheSummary->NumCutoffEntries = Res.size();
|
||||
TheSummary->set(Summary::MaxFunctionCount, PS.getMaxFunctionCount());
|
||||
TheSummary->set(Summary::MaxBlockCount, PS.getMaxCount());
|
||||
TheSummary->set(Summary::MaxInternalBlockCount, PS.getMaxInternalCount());
|
||||
TheSummary->set(Summary::TotalBlockCount, PS.getTotalCount());
|
||||
TheSummary->set(Summary::TotalNumBlocks, PS.getNumCounts());
|
||||
TheSummary->set(Summary::TotalNumFunctions, PS.getNumFunctions());
|
||||
for (unsigned I = 0; I < Res.size(); I++)
|
||||
TheSummary->setEntry(I, Res[I]);
|
||||
}
|
||||
|
||||
void InstrProfWriter::writeImpl(ProfOStream &OS) {
|
||||
using namespace IndexedInstrProf;
|
||||
|
||||
OnDiskChainedHashTableGenerator<InstrProfRecordWriterTrait> Generator;
|
||||
|
||||
InstrProfSummaryBuilder ISB(ProfileSummaryBuilder::DefaultCutoffs);
|
||||
InfoObj->SummaryBuilder = &ISB;
|
||||
|
||||
// Populate the hash table generator.
|
||||
for (const auto &I : FunctionData)
|
||||
if (shouldEncodeData(I.getValue()))
|
||||
Generator.insert(I.getKey(), &I.getValue());
|
||||
// Write the header.
|
||||
IndexedInstrProf::Header Header;
|
||||
Header.Magic = IndexedInstrProf::Magic;
|
||||
Header.Version = IndexedInstrProf::ProfVersion::CurrentVersion;
|
||||
if (ProfileKind == PF_IRLevel)
|
||||
Header.Version |= VARIANT_MASK_IR_PROF;
|
||||
Header.Unused = 0;
|
||||
Header.HashType = static_cast<uint64_t>(IndexedInstrProf::HashType);
|
||||
Header.HashOffset = 0;
|
||||
int N = sizeof(IndexedInstrProf::Header) / sizeof(uint64_t);
|
||||
|
||||
// Only write out all the fields except 'HashOffset'. We need
|
||||
// to remember the offset of that field to allow back patching
|
||||
// later.
|
||||
for (int I = 0; I < N - 1; I++)
|
||||
OS.write(reinterpret_cast<uint64_t *>(&Header)[I]);
|
||||
|
||||
// Save the location of Header.HashOffset field in \c OS.
|
||||
uint64_t HashTableStartFieldOffset = OS.tell();
|
||||
// Reserve the space for HashOffset field.
|
||||
OS.write(0);
|
||||
|
||||
// Reserve space to write profile summary data.
|
||||
uint32_t NumEntries = ProfileSummaryBuilder::DefaultCutoffs.size();
|
||||
uint32_t SummarySize = Summary::getSize(Summary::NumKinds, NumEntries);
|
||||
// Remember the summary offset.
|
||||
uint64_t SummaryOffset = OS.tell();
|
||||
for (unsigned I = 0; I < SummarySize / sizeof(uint64_t); I++)
|
||||
OS.write(0);
|
||||
|
||||
// Write the hash table.
|
||||
uint64_t HashTableStart = Generator.Emit(OS.OS, *InfoObj);
|
||||
|
||||
// Allocate space for data to be serialized out.
|
||||
std::unique_ptr<IndexedInstrProf::Summary> TheSummary =
|
||||
IndexedInstrProf::allocSummary(SummarySize);
|
||||
// Compute the Summary and copy the data to the data
|
||||
// structure to be serialized out (to disk or buffer).
|
||||
std::unique_ptr<ProfileSummary> PS = ISB.getSummary();
|
||||
setSummary(TheSummary.get(), *PS);
|
||||
InfoObj->SummaryBuilder = nullptr;
|
||||
|
||||
// Now do the final patch:
|
||||
PatchItem PatchItems[] = {
|
||||
// Patch the Header.HashOffset field.
|
||||
{HashTableStartFieldOffset, &HashTableStart, 1},
|
||||
// Patch the summary data.
|
||||
{SummaryOffset, reinterpret_cast<uint64_t *>(TheSummary.get()),
|
||||
(int)(SummarySize / sizeof(uint64_t))}};
|
||||
OS.patch(PatchItems, sizeof(PatchItems) / sizeof(*PatchItems));
|
||||
}
|
||||
|
||||
void InstrProfWriter::write(raw_fd_ostream &OS) {
|
||||
// Write the hash table.
|
||||
ProfOStream POS(OS);
|
||||
writeImpl(POS);
|
||||
}
|
||||
|
||||
std::unique_ptr<MemoryBuffer> InstrProfWriter::writeBuffer() {
|
||||
std::string Data;
|
||||
raw_string_ostream OS(Data);
|
||||
ProfOStream POS(OS);
|
||||
// Write the hash table.
|
||||
writeImpl(POS);
|
||||
// Return this in an aligned memory buffer.
|
||||
return MemoryBuffer::getMemBufferCopy(Data);
|
||||
}
|
||||
|
||||
static const char *ValueProfKindStr[] = {
|
||||
#define VALUE_PROF_KIND(Enumerator, Value) #Enumerator,
|
||||
#include "llvm/ProfileData/InstrProfData.inc"
|
||||
};
|
||||
|
||||
void InstrProfWriter::writeRecordInText(StringRef Name, uint64_t Hash,
|
||||
const InstrProfRecord &Func,
|
||||
InstrProfSymtab &Symtab,
|
||||
raw_fd_ostream &OS) {
|
||||
OS << Name << "\n";
|
||||
OS << "# Func Hash:\n" << Hash << "\n";
|
||||
OS << "# Num Counters:\n" << Func.Counts.size() << "\n";
|
||||
OS << "# Counter Values:\n";
|
||||
for (uint64_t Count : Func.Counts)
|
||||
OS << Count << "\n";
|
||||
|
||||
uint32_t NumValueKinds = Func.getNumValueKinds();
|
||||
if (!NumValueKinds) {
|
||||
OS << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
OS << "# Num Value Kinds:\n" << Func.getNumValueKinds() << "\n";
|
||||
for (uint32_t VK = 0; VK < IPVK_Last + 1; VK++) {
|
||||
uint32_t NS = Func.getNumValueSites(VK);
|
||||
if (!NS)
|
||||
continue;
|
||||
OS << "# ValueKind = " << ValueProfKindStr[VK] << ":\n" << VK << "\n";
|
||||
OS << "# NumValueSites:\n" << NS << "\n";
|
||||
for (uint32_t S = 0; S < NS; S++) {
|
||||
uint32_t ND = Func.getNumValueDataForSite(VK, S);
|
||||
OS << ND << "\n";
|
||||
std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, S);
|
||||
for (uint32_t I = 0; I < ND; I++) {
|
||||
if (VK == IPVK_IndirectCallTarget)
|
||||
OS << Symtab.getFuncName(VD[I].Value) << ":" << VD[I].Count << "\n";
|
||||
else
|
||||
OS << VD[I].Value << ":" << VD[I].Count << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OS << "\n";
|
||||
}
|
||||
|
||||
Error InstrProfWriter::writeText(raw_fd_ostream &OS) {
|
||||
if (ProfileKind == PF_IRLevel)
|
||||
OS << "# IR level Instrumentation Flag\n:ir\n";
|
||||
InstrProfSymtab Symtab;
|
||||
for (const auto &I : FunctionData)
|
||||
if (shouldEncodeData(I.getValue()))
|
||||
if (Error E = Symtab.addFuncName(I.getKey()))
|
||||
return E;
|
||||
Symtab.finalizeSymtab();
|
||||
|
||||
for (const auto &I : FunctionData)
|
||||
if (shouldEncodeData(I.getValue()))
|
||||
for (const auto &Func : I.getValue())
|
||||
writeRecordInText(I.getKey(), Func.first, Func.second, Symtab, OS);
|
||||
return Error::success();
|
||||
}
|
25
external/llvm/lib/ProfileData/LLVMBuild.txt
vendored
Normal file
25
external/llvm/lib/ProfileData/LLVMBuild.txt
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
;===- ./lib/ProfileData/LLVMBuild.txt --------------------------*- Conf -*--===;
|
||||
;
|
||||
; The LLVM Compiler Infrastructure
|
||||
;
|
||||
; This file is distributed under the University of Illinois Open Source
|
||||
; License. See LICENSE.TXT for details.
|
||||
;
|
||||
;===------------------------------------------------------------------------===;
|
||||
;
|
||||
; This is an LLVMBuild description file for the components in this subdirectory.
|
||||
;
|
||||
; For more information on the LLVMBuild system, please see:
|
||||
;
|
||||
; http://llvm.org/docs/LLVMBuild.html
|
||||
;
|
||||
;===------------------------------------------------------------------------===;
|
||||
|
||||
[common]
|
||||
subdirectories = Coverage
|
||||
|
||||
[component_0]
|
||||
type = Library
|
||||
name = ProfileData
|
||||
parent = Libraries
|
||||
required_libraries = Core Support
|
115
external/llvm/lib/ProfileData/ProfileSummaryBuilder.cpp
vendored
Normal file
115
external/llvm/lib/ProfileData/ProfileSummaryBuilder.cpp
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
//=-- ProfilesummaryBuilder.cpp - Profile summary computation ---------------=//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains support for computing profile summary data.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/IR/Attributes.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/Metadata.h"
|
||||
#include "llvm/IR/Type.h"
|
||||
#include "llvm/ProfileData/InstrProf.h"
|
||||
#include "llvm/ProfileData/ProfileCommon.h"
|
||||
#include "llvm/ProfileData/SampleProf.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
// A set of cutoff values. Each value, when divided by ProfileSummary::Scale
|
||||
// (which is 1000000) is a desired percentile of total counts.
|
||||
static const uint32_t DefaultCutoffsData[] = {
|
||||
10000, /* 1% */
|
||||
100000, /* 10% */
|
||||
200000, 300000, 400000, 500000, 600000, 500000, 600000, 700000,
|
||||
800000, 900000, 950000, 990000, 999000, 999900, 999990, 999999};
|
||||
const ArrayRef<uint32_t> ProfileSummaryBuilder::DefaultCutoffs =
|
||||
DefaultCutoffsData;
|
||||
|
||||
void InstrProfSummaryBuilder::addRecord(const InstrProfRecord &R) {
|
||||
// The first counter is not necessarily an entry count for IR
|
||||
// instrumentation profiles.
|
||||
// Eventually MaxFunctionCount will become obsolete and this can be
|
||||
// removed.
|
||||
addEntryCount(R.Counts[0]);
|
||||
for (size_t I = 1, E = R.Counts.size(); I < E; ++I)
|
||||
addInternalCount(R.Counts[I]);
|
||||
}
|
||||
|
||||
// To compute the detailed summary, we consider each line containing samples as
|
||||
// equivalent to a block with a count in the instrumented profile.
|
||||
void SampleProfileSummaryBuilder::addRecord(
|
||||
const sampleprof::FunctionSamples &FS) {
|
||||
NumFunctions++;
|
||||
if (FS.getHeadSamples() > MaxFunctionCount)
|
||||
MaxFunctionCount = FS.getHeadSamples();
|
||||
for (const auto &I : FS.getBodySamples())
|
||||
addCount(I.second.getSamples());
|
||||
}
|
||||
|
||||
// The argument to this method is a vector of cutoff percentages and the return
|
||||
// value is a vector of (Cutoff, MinCount, NumCounts) triplets.
|
||||
void ProfileSummaryBuilder::computeDetailedSummary() {
|
||||
if (DetailedSummaryCutoffs.empty())
|
||||
return;
|
||||
std::sort(DetailedSummaryCutoffs.begin(), DetailedSummaryCutoffs.end());
|
||||
auto Iter = CountFrequencies.begin();
|
||||
const auto End = CountFrequencies.end();
|
||||
|
||||
uint32_t CountsSeen = 0;
|
||||
uint64_t CurrSum = 0, Count = 0;
|
||||
|
||||
for (const uint32_t Cutoff : DetailedSummaryCutoffs) {
|
||||
assert(Cutoff <= 999999);
|
||||
APInt Temp(128, TotalCount);
|
||||
APInt N(128, Cutoff);
|
||||
APInt D(128, ProfileSummary::Scale);
|
||||
Temp *= N;
|
||||
Temp = Temp.sdiv(D);
|
||||
uint64_t DesiredCount = Temp.getZExtValue();
|
||||
assert(DesiredCount <= TotalCount);
|
||||
while (CurrSum < DesiredCount && Iter != End) {
|
||||
Count = Iter->first;
|
||||
uint32_t Freq = Iter->second;
|
||||
CurrSum += (Count * Freq);
|
||||
CountsSeen += Freq;
|
||||
Iter++;
|
||||
}
|
||||
assert(CurrSum >= DesiredCount);
|
||||
ProfileSummaryEntry PSE = {Cutoff, Count, CountsSeen};
|
||||
DetailedSummary.push_back(PSE);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ProfileSummary> SampleProfileSummaryBuilder::getSummary() {
|
||||
computeDetailedSummary();
|
||||
return llvm::make_unique<ProfileSummary>(
|
||||
ProfileSummary::PSK_Sample, DetailedSummary, TotalCount, MaxCount, 0,
|
||||
MaxFunctionCount, NumCounts, NumFunctions);
|
||||
}
|
||||
|
||||
std::unique_ptr<ProfileSummary> InstrProfSummaryBuilder::getSummary() {
|
||||
computeDetailedSummary();
|
||||
return llvm::make_unique<ProfileSummary>(
|
||||
ProfileSummary::PSK_Instr, DetailedSummary, TotalCount, MaxCount,
|
||||
MaxInternalBlockCount, MaxFunctionCount, NumCounts, NumFunctions);
|
||||
}
|
||||
|
||||
void InstrProfSummaryBuilder::addEntryCount(uint64_t Count) {
|
||||
addCount(Count);
|
||||
NumFunctions++;
|
||||
if (Count > MaxFunctionCount)
|
||||
MaxFunctionCount = Count;
|
||||
}
|
||||
|
||||
void InstrProfSummaryBuilder::addInternalCount(uint64_t Count) {
|
||||
addCount(Count);
|
||||
if (Count > MaxInternalBlockCount)
|
||||
MaxInternalBlockCount = Count;
|
||||
}
|
155
external/llvm/lib/ProfileData/SampleProf.cpp
vendored
Normal file
155
external/llvm/lib/ProfileData/SampleProf.cpp
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
//=-- SampleProf.cpp - Sample profiling format support --------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains common definitions used in the reading and writing of
|
||||
// sample profile data.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ProfileData/SampleProf.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace sampleprof;
|
||||
|
||||
namespace {
|
||||
|
||||
// FIXME: This class is only here to support the transition to llvm::Error. It
|
||||
// will be removed once this transition is complete. Clients should prefer to
|
||||
// deal with the Error value directly, rather than converting to error_code.
|
||||
class SampleProfErrorCategoryType : public std::error_category {
|
||||
const char *name() const noexcept override { return "llvm.sampleprof"; }
|
||||
|
||||
std::string message(int IE) const override {
|
||||
sampleprof_error E = static_cast<sampleprof_error>(IE);
|
||||
switch (E) {
|
||||
case sampleprof_error::success:
|
||||
return "Success";
|
||||
case sampleprof_error::bad_magic:
|
||||
return "Invalid sample profile data (bad magic)";
|
||||
case sampleprof_error::unsupported_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 sample profile data";
|
||||
case sampleprof_error::unrecognized_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:
|
||||
return "Truncated function name table";
|
||||
case sampleprof_error::not_implemented:
|
||||
return "Unimplemented feature";
|
||||
case sampleprof_error::counter_overflow:
|
||||
return "Counter overflow";
|
||||
}
|
||||
llvm_unreachable("A value of sampleprof_error has no message.");
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
static ManagedStatic<SampleProfErrorCategoryType> ErrorCategory;
|
||||
|
||||
const std::error_category &llvm::sampleprof_category() {
|
||||
return *ErrorCategory;
|
||||
}
|
||||
|
||||
void LineLocation::print(raw_ostream &OS) const {
|
||||
OS << LineOffset;
|
||||
if (Discriminator > 0)
|
||||
OS << "." << Discriminator;
|
||||
}
|
||||
|
||||
raw_ostream &llvm::sampleprof::operator<<(raw_ostream &OS,
|
||||
const LineLocation &Loc) {
|
||||
Loc.print(OS);
|
||||
return OS;
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
||||
LLVM_DUMP_METHOD void LineLocation::dump() const { print(dbgs()); }
|
||||
#endif
|
||||
|
||||
/// \brief Print the sample record to the stream \p OS indented by \p Indent.
|
||||
void SampleRecord::print(raw_ostream &OS, unsigned Indent) const {
|
||||
OS << NumSamples;
|
||||
if (hasCalls()) {
|
||||
OS << ", calls:";
|
||||
for (const auto &I : getCallTargets())
|
||||
OS << " " << I.first() << ":" << I.second;
|
||||
}
|
||||
OS << "\n";
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
||||
LLVM_DUMP_METHOD void SampleRecord::dump() const { print(dbgs(), 0); }
|
||||
#endif
|
||||
|
||||
raw_ostream &llvm::sampleprof::operator<<(raw_ostream &OS,
|
||||
const SampleRecord &Sample) {
|
||||
Sample.print(OS, 0);
|
||||
return OS;
|
||||
}
|
||||
|
||||
/// \brief Print the samples collected for a function on stream \p OS.
|
||||
void FunctionSamples::print(raw_ostream &OS, unsigned Indent) const {
|
||||
OS << TotalSamples << ", " << TotalHeadSamples << ", " << BodySamples.size()
|
||||
<< " sampled lines\n";
|
||||
|
||||
OS.indent(Indent);
|
||||
if (!BodySamples.empty()) {
|
||||
OS << "Samples collected in the function's body {\n";
|
||||
SampleSorter<LineLocation, SampleRecord> SortedBodySamples(BodySamples);
|
||||
for (const auto &SI : SortedBodySamples.get()) {
|
||||
OS.indent(Indent + 2);
|
||||
OS << SI->first << ": " << SI->second;
|
||||
}
|
||||
OS.indent(Indent);
|
||||
OS << "}\n";
|
||||
} else {
|
||||
OS << "No samples collected in the function's body\n";
|
||||
}
|
||||
|
||||
OS.indent(Indent);
|
||||
if (!CallsiteSamples.empty()) {
|
||||
OS << "Samples collected in inlined callsites {\n";
|
||||
SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(
|
||||
CallsiteSamples);
|
||||
for (const auto &CS : SortedCallsiteSamples.get()) {
|
||||
for (const auto &FS : CS->second) {
|
||||
OS.indent(Indent + 2);
|
||||
OS << CS->first << ": inlined callee: " << FS.second.getName() << ": ";
|
||||
FS.second.print(OS, Indent + 4);
|
||||
}
|
||||
}
|
||||
OS << "}\n";
|
||||
} else {
|
||||
OS << "No inlined callsites in this function\n";
|
||||
}
|
||||
}
|
||||
|
||||
raw_ostream &llvm::sampleprof::operator<<(raw_ostream &OS,
|
||||
const FunctionSamples &FS) {
|
||||
FS.print(OS);
|
||||
return OS;
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
||||
LLVM_DUMP_METHOD void FunctionSamples::dump() const { print(dbgs(), 0); }
|
||||
#endif
|
807
external/llvm/lib/ProfileData/SampleProfReader.cpp
vendored
Normal file
807
external/llvm/lib/ProfileData/SampleProfReader.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
307
external/llvm/lib/ProfileData/SampleProfWriter.cpp
vendored
Normal file
307
external/llvm/lib/ProfileData/SampleProfWriter.cpp
vendored
Normal file
@ -0,0 +1,307 @@
|
||||
//===- SampleProfWriter.cpp - Write LLVM sample profile data --------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the class that writes LLVM sample profiles. It
|
||||
// supports two file formats: text and binary. The textual representation
|
||||
// is useful for debugging and testing purposes. The binary representation
|
||||
// is more compact, resulting in smaller file sizes. However, they can
|
||||
// both be used interchangeably.
|
||||
//
|
||||
// See lib/ProfileData/SampleProfReader.cpp for documentation on each of the
|
||||
// supported formats.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ProfileData/SampleProfWriter.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ProfileData/ProfileCommon.h"
|
||||
#include "llvm/ProfileData/SampleProf.h"
|
||||
#include "llvm/Support/ErrorOr.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/LEB128.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace sampleprof;
|
||||
|
||||
std::error_code
|
||||
SampleProfileWriter::write(const StringMap<FunctionSamples> &ProfileMap) {
|
||||
if (std::error_code EC = writeHeader(ProfileMap))
|
||||
return EC;
|
||||
|
||||
// Sort the ProfileMap by total samples.
|
||||
typedef std::pair<StringRef, const FunctionSamples *> NameFunctionSamples;
|
||||
std::vector<NameFunctionSamples> V;
|
||||
for (const auto &I : ProfileMap)
|
||||
V.push_back(std::make_pair(I.getKey(), &I.second));
|
||||
|
||||
std::stable_sort(
|
||||
V.begin(), V.end(),
|
||||
[](const NameFunctionSamples &A, const NameFunctionSamples &B) {
|
||||
if (A.second->getTotalSamples() == B.second->getTotalSamples())
|
||||
return A.first > B.first;
|
||||
return A.second->getTotalSamples() > B.second->getTotalSamples();
|
||||
});
|
||||
|
||||
for (const auto &I : V) {
|
||||
if (std::error_code EC = write(*I.second))
|
||||
return EC;
|
||||
}
|
||||
return sampleprof_error::success;
|
||||
}
|
||||
|
||||
/// \brief Write samples to a text file.
|
||||
///
|
||||
/// Note: it may be tempting to implement this in terms of
|
||||
/// FunctionSamples::print(). Please don't. The dump functionality is intended
|
||||
/// for debugging and has no specified form.
|
||||
///
|
||||
/// The format used here is more structured and deliberate because
|
||||
/// it needs to be parsed by the SampleProfileReaderText class.
|
||||
std::error_code SampleProfileWriterText::write(const FunctionSamples &S) {
|
||||
auto &OS = *OutputStream;
|
||||
OS << S.getName() << ":" << S.getTotalSamples();
|
||||
if (Indent == 0)
|
||||
OS << ":" << S.getHeadSamples();
|
||||
OS << "\n";
|
||||
|
||||
SampleSorter<LineLocation, SampleRecord> SortedSamples(S.getBodySamples());
|
||||
for (const auto &I : SortedSamples.get()) {
|
||||
LineLocation Loc = I->first;
|
||||
const SampleRecord &Sample = I->second;
|
||||
OS.indent(Indent + 1);
|
||||
if (Loc.Discriminator == 0)
|
||||
OS << Loc.LineOffset << ": ";
|
||||
else
|
||||
OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
|
||||
|
||||
OS << Sample.getSamples();
|
||||
|
||||
for (const auto &J : Sample.getCallTargets())
|
||||
OS << " " << J.first() << ":" << J.second;
|
||||
OS << "\n";
|
||||
}
|
||||
|
||||
SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(
|
||||
S.getCallsiteSamples());
|
||||
Indent += 1;
|
||||
for (const auto &I : SortedCallsiteSamples.get())
|
||||
for (const auto &FS : I->second) {
|
||||
LineLocation Loc = I->first;
|
||||
const FunctionSamples &CalleeSamples = FS.second;
|
||||
OS.indent(Indent);
|
||||
if (Loc.Discriminator == 0)
|
||||
OS << Loc.LineOffset << ": ";
|
||||
else
|
||||
OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
|
||||
if (std::error_code EC = write(CalleeSamples))
|
||||
return EC;
|
||||
}
|
||||
Indent -= 1;
|
||||
|
||||
return sampleprof_error::success;
|
||||
}
|
||||
|
||||
std::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName) {
|
||||
const auto &ret = NameTable.find(FName);
|
||||
if (ret == NameTable.end())
|
||||
return sampleprof_error::truncated_name_table;
|
||||
encodeULEB128(ret->second, *OutputStream);
|
||||
return sampleprof_error::success;
|
||||
}
|
||||
|
||||
void SampleProfileWriterBinary::addName(StringRef FName) {
|
||||
NameTable.insert(std::make_pair(FName, 0));
|
||||
}
|
||||
|
||||
void SampleProfileWriterBinary::addNames(const FunctionSamples &S) {
|
||||
// Add all the names in indirect call targets.
|
||||
for (const auto &I : S.getBodySamples()) {
|
||||
const SampleRecord &Sample = I.second;
|
||||
for (const auto &J : Sample.getCallTargets())
|
||||
addName(J.first());
|
||||
}
|
||||
|
||||
// Recursively add all the names for inlined callsites.
|
||||
for (const auto &J : S.getCallsiteSamples())
|
||||
for (const auto &FS : J.second) {
|
||||
const FunctionSamples &CalleeSamples = FS.second;
|
||||
addName(CalleeSamples.getName());
|
||||
addNames(CalleeSamples);
|
||||
}
|
||||
}
|
||||
|
||||
std::error_code SampleProfileWriterBinary::writeHeader(
|
||||
const StringMap<FunctionSamples> &ProfileMap) {
|
||||
auto &OS = *OutputStream;
|
||||
|
||||
// Write file magic identifier.
|
||||
encodeULEB128(SPMagic(), OS);
|
||||
encodeULEB128(SPVersion(), OS);
|
||||
|
||||
computeSummary(ProfileMap);
|
||||
if (auto EC = writeSummary())
|
||||
return EC;
|
||||
|
||||
// Generate the name table for all the functions referenced in the profile.
|
||||
for (const auto &I : ProfileMap) {
|
||||
addName(I.first());
|
||||
addNames(I.second);
|
||||
}
|
||||
|
||||
// Sort the names to make NameTable is deterministic.
|
||||
std::set<StringRef> V;
|
||||
for (const auto &I : NameTable)
|
||||
V.insert(I.first);
|
||||
int i = 0;
|
||||
for (const StringRef &N : V)
|
||||
NameTable[N] = i++;
|
||||
|
||||
// Write out the name table.
|
||||
encodeULEB128(NameTable.size(), OS);
|
||||
for (auto N : V) {
|
||||
OS << N;
|
||||
encodeULEB128(0, OS);
|
||||
}
|
||||
return sampleprof_error::success;
|
||||
}
|
||||
|
||||
std::error_code SampleProfileWriterBinary::writeSummary() {
|
||||
auto &OS = *OutputStream;
|
||||
encodeULEB128(Summary->getTotalCount(), OS);
|
||||
encodeULEB128(Summary->getMaxCount(), OS);
|
||||
encodeULEB128(Summary->getMaxFunctionCount(), OS);
|
||||
encodeULEB128(Summary->getNumCounts(), OS);
|
||||
encodeULEB128(Summary->getNumFunctions(), OS);
|
||||
std::vector<ProfileSummaryEntry> &Entries = Summary->getDetailedSummary();
|
||||
encodeULEB128(Entries.size(), OS);
|
||||
for (auto Entry : Entries) {
|
||||
encodeULEB128(Entry.Cutoff, OS);
|
||||
encodeULEB128(Entry.MinCount, OS);
|
||||
encodeULEB128(Entry.NumCounts, OS);
|
||||
}
|
||||
return sampleprof_error::success;
|
||||
}
|
||||
std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {
|
||||
auto &OS = *OutputStream;
|
||||
|
||||
if (std::error_code EC = writeNameIdx(S.getName()))
|
||||
return EC;
|
||||
|
||||
encodeULEB128(S.getTotalSamples(), OS);
|
||||
|
||||
// Emit all the body samples.
|
||||
encodeULEB128(S.getBodySamples().size(), OS);
|
||||
for (const auto &I : S.getBodySamples()) {
|
||||
LineLocation Loc = I.first;
|
||||
const SampleRecord &Sample = I.second;
|
||||
encodeULEB128(Loc.LineOffset, OS);
|
||||
encodeULEB128(Loc.Discriminator, OS);
|
||||
encodeULEB128(Sample.getSamples(), OS);
|
||||
encodeULEB128(Sample.getCallTargets().size(), OS);
|
||||
for (const auto &J : Sample.getCallTargets()) {
|
||||
StringRef Callee = J.first();
|
||||
uint64_t CalleeSamples = J.second;
|
||||
if (std::error_code EC = writeNameIdx(Callee))
|
||||
return EC;
|
||||
encodeULEB128(CalleeSamples, OS);
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively emit all the callsite samples.
|
||||
uint64_t NumCallsites = 0;
|
||||
for (const auto &J : S.getCallsiteSamples())
|
||||
NumCallsites += J.second.size();
|
||||
encodeULEB128(NumCallsites, OS);
|
||||
for (const auto &J : S.getCallsiteSamples())
|
||||
for (const auto &FS : J.second) {
|
||||
LineLocation Loc = J.first;
|
||||
const FunctionSamples &CalleeSamples = FS.second;
|
||||
encodeULEB128(Loc.LineOffset, OS);
|
||||
encodeULEB128(Loc.Discriminator, OS);
|
||||
if (std::error_code EC = writeBody(CalleeSamples))
|
||||
return EC;
|
||||
}
|
||||
|
||||
return sampleprof_error::success;
|
||||
}
|
||||
|
||||
/// \brief Write samples of a top-level function to a binary file.
|
||||
///
|
||||
/// \returns true if the samples were written successfully, false otherwise.
|
||||
std::error_code SampleProfileWriterBinary::write(const FunctionSamples &S) {
|
||||
encodeULEB128(S.getHeadSamples(), *OutputStream);
|
||||
return writeBody(S);
|
||||
}
|
||||
|
||||
/// \brief Create a sample profile file writer based on the specified format.
|
||||
///
|
||||
/// \param Filename The file to create.
|
||||
///
|
||||
/// \param Format Encoding format for the profile file.
|
||||
///
|
||||
/// \returns an error code indicating the status of the created writer.
|
||||
ErrorOr<std::unique_ptr<SampleProfileWriter>>
|
||||
SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) {
|
||||
std::error_code EC;
|
||||
std::unique_ptr<raw_ostream> OS;
|
||||
if (Format == SPF_Binary)
|
||||
OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::F_None));
|
||||
else
|
||||
OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::F_Text));
|
||||
if (EC)
|
||||
return EC;
|
||||
|
||||
return create(OS, Format);
|
||||
}
|
||||
|
||||
/// \brief Create a sample profile stream writer based on the specified format.
|
||||
///
|
||||
/// \param OS The output stream to store the profile data to.
|
||||
///
|
||||
/// \param Format Encoding format for the profile file.
|
||||
///
|
||||
/// \returns an error code indicating the status of the created writer.
|
||||
ErrorOr<std::unique_ptr<SampleProfileWriter>>
|
||||
SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
|
||||
SampleProfileFormat Format) {
|
||||
std::error_code EC;
|
||||
std::unique_ptr<SampleProfileWriter> Writer;
|
||||
|
||||
if (Format == SPF_Binary)
|
||||
Writer.reset(new SampleProfileWriterBinary(OS));
|
||||
else if (Format == SPF_Text)
|
||||
Writer.reset(new SampleProfileWriterText(OS));
|
||||
else if (Format == SPF_GCC)
|
||||
EC = sampleprof_error::unsupported_writing_format;
|
||||
else
|
||||
EC = sampleprof_error::unrecognized_format;
|
||||
|
||||
if (EC)
|
||||
return EC;
|
||||
|
||||
return std::move(Writer);
|
||||
}
|
||||
|
||||
void SampleProfileWriter::computeSummary(
|
||||
const StringMap<FunctionSamples> &ProfileMap) {
|
||||
SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
|
||||
for (const auto &I : ProfileMap) {
|
||||
const FunctionSamples &Profile = I.second;
|
||||
Builder.addRecord(Profile);
|
||||
}
|
||||
Summary = Builder.getSummary();
|
||||
}
|
Reference in New Issue
Block a user