Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions gcov.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
ABSL_FLAG(uint64_t, gcov_version, 0x3430372a,
"Gcov version number.");

const uint32 GCOV_TAG_AFDO_SUMMARY = 0xa8000000;
const uint32 GCOV_TAG_AFDO_FILE_NAMES = 0xaa000000;
const uint32 GCOV_TAG_AFDO_FUNCTION = 0xac000000;
const uint32 GCOV_TAG_MODULE_GROUPING = 0xae000000;
Expand Down Expand Up @@ -149,7 +150,7 @@ void gcov_write_string(const char *string) {

if (string) {
length = strlen(string);
if (absl::GetFlag(FLAGS_gcov_version) == 2) {
if (absl::GetFlag(FLAGS_gcov_version) >= 2) {
// Length includes the terminating 0 and is saved in bytes.
alloc = length + 1;
char *byte_buffer = gcov_write_bytes(4 + alloc);
Expand Down Expand Up @@ -229,7 +230,7 @@ const char * gcov_read_string(void) {
return 0;
}

if (absl::GetFlag(FLAGS_gcov_version) == 2) {
if (absl::GetFlag(FLAGS_gcov_version) >= 2) {
return gcov_read_bytes (length);
}
else {
Expand Down
1 change: 1 addition & 0 deletions gcov.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "base/common.h"
#include "third_party/abseil/absl/flags/declare.h"

extern const uint32 GCOV_TAG_AFDO_SUMMARY;
extern const uint32 GCOV_TAG_AFDO_FILE_NAMES;
extern const uint32 GCOV_TAG_AFDO_FUNCTION;
extern const uint32 GCOV_TAG_MODULE_GROUPING;
Expand Down
18 changes: 18 additions & 0 deletions profile_reader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,23 @@ void AutoFDOProfileReader::ReadSymbolProfile(const SourceStack &stack,
}
}

void AutoFDOProfileReader::ReadSummary() {
if (absl::GetFlag(FLAGS_gcov_version) >= 3) {
CHECK_EQ(gcov_read_unsigned(), GCOV_TAG_AFDO_SUMMARY);
gcov_read_counter(); // Total count
gcov_read_counter(); // Max count
gcov_read_counter(); // Max function count
gcov_read_counter(); // Num counts
gcov_read_counter(); // Num functions
unsigned num = gcov_read_counter();
for (unsigned i = 0; i < num; i++) {
gcov_read_unsigned(); // Cutoff
gcov_read_counter(); // Min count
gcov_read_counter(); // Num cutoffs
}
}
}

void AutoFDOProfileReader::ReadNameTable() {
CHECK_EQ(gcov_read_unsigned(), GCOV_TAG_AFDO_FILE_NAMES);
gcov_read_unsigned();
Expand Down Expand Up @@ -115,6 +132,7 @@ bool AutoFDOProfileReader::ReadFromFile(const std::string &output_file) {
absl::SetFlag(&FLAGS_gcov_version, gcov_read_unsigned());
gcov_read_unsigned();

ReadSummary();
ReadNameTable();
ReadFunctionProfile();
ReadModuleGroup();
Expand Down
1 change: 1 addition & 0 deletions profile_reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class AutoFDOProfileReader : public ProfileReader {
// where symbol_map was built purely from profile thus alias symbol info
// is not available. In that case, we should always update the symbol.
void ReadSymbolProfile(const SourceStack &stack, bool update);
void ReadSummary();
void ReadNameTable();

SymbolMap *symbol_map_;
Expand Down
79 changes: 79 additions & 0 deletions profile_writer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,27 @@ void AutoFDOProfileWriter::WriteFunctionProfile() {

StringTableUpdater::Update(*symbol_map_, &string_index_map);

ProfileSummaryInformation info = ProfileSummaryComputer::Compute(
*symbol_map_, {std::begin(ProfileSummaryInformation::default_cutoffs),
std::end(ProfileSummaryInformation::default_cutoffs)});

// Write out the GCOV_TAG_AFDO_SUMMARY section.
if (absl::GetFlag(FLAGS_gcov_version) >= 3) {
assert(summary != nullptr);
gcov_write_unsigned(GCOV_TAG_AFDO_SUMMARY);
gcov_write_counter(info.total_count_);
gcov_write_counter(info.max_count_);
gcov_write_counter(info.max_function_count_);
gcov_write_counter(info.num_counts_);
gcov_write_counter(info.num_functions_);
gcov_write_counter(info.detailed_summaries_.size());
for (const auto &detailed_summary : info.detailed_summaries_) {
gcov_write_unsigned(detailed_summary.cutoff_);
gcov_write_counter(detailed_summary.min_count_);
gcov_write_counter(detailed_summary.num_counts_);
}
}

for (auto &name_index : string_index_map) {
name_index.second = current_name_index++;
length_4bytes += (name_index.first.size()
Expand Down Expand Up @@ -227,6 +248,64 @@ bool AutoFDOProfileWriter::WriteToFile(const std::string &output_filename) {
return true;
}

ProfileSummaryComputer::ProfileSummaryComputer()
: cutoffs_{std::begin(ProfileSummaryInformation::default_cutoffs),
std::end(ProfileSummaryInformation::default_cutoffs)} {}

ProfileSummaryComputer::ProfileSummaryComputer(std::vector<uint32_t> cutoffs)
: cutoffs_{std::move(cutoffs)} {}

void ProfileSummaryComputer::VisitTopSymbol(const std::string &name,
const Symbol *node) {
info_.num_functions_++;
info_.max_function_count_ =
std::max(info_.max_function_count_, node->head_count);
}

void ProfileSummaryComputer::Visit(const Symbol *node) {
// There is a slight difference against the values computed by
// SampleProfileSummaryBuilder/LLVMProfileBuilder as it represents
// lineno:discriminator pairs as 16:32 bits. This causes line numbers >=
// UINT16_MAX to be counted incorrectly (see GetLineNumberFromOffset in
// source_info.h) as they collide with line numbers < UINT16_MAX. This issue
// is completely avoided here by just not using the offset info at all.
for (auto &pos_count : node->pos_counts) {
uint64_t count = pos_count.second.count;
info_.total_count_ += count;
info_.max_count_ = std::max(info_.max_count_, count);
info_.num_counts_++;
count_frequencies_[count]++;
}
}

void ProfileSummaryComputer::ComputeDetailedSummary() {
auto iter = count_frequencies_.begin();
auto end = count_frequencies_.end();

uint32_t counts_seen = 0;
uint64_t curr_sum = 0;
uint64_t count = 0;

for (const uint32_t cutoff : cutoffs_) {
assert(cutoff <= 999'999);
constexpr int scale = 1'000'000;
using uint128_t = unsigned __int128;
uint128_t desired_count = info_.total_count_;
desired_count = desired_count * uint128_t(cutoff);
desired_count = desired_count / uint128_t(scale);
assert(desired_count <= info_.total_count_);
while (curr_sum < desired_count && iter != end) {
count = iter->first;
uint32_t freq = iter->second;
curr_sum += (count * freq);
counts_seen += freq;
iter++;
}
assert(curr_sum >= desired_count);
info_.detailed_summaries_.push_back({cutoff, count, counts_seen});
}
}

// Debugging support. ProfileDumper emits a detailed dump of the contents
// of the input profile.
class ProfileDumper : public SymbolTraverser {
Expand Down
62 changes: 62 additions & 0 deletions profile_writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,68 @@ class StringTableUpdater: public SymbolTraverser {
StringIndexMap *map_;
};

class ProfileSummaryInformation {
public:
static constexpr std::array<uint32_t, 16> default_cutoffs = {
10000, 100000, 200000, 300000, 400000, 500000, 600000, 700000,
800000, 900000, 950000, 990000, 999000, 999900, 999990, 999999};

// The detailed summary is a histogram-based calculation of the minimum
// execution count required to belong to a certain set of percentile of
// counts.
struct DetailedSummary {
// The percentile that this represents (multiplied by 1,000,000).
uint32_t cutoff_{};
// The minimum execution count required to belong to this percentile.
uint64_t min_count_{};
// The number of samples which belong to this percentile.
uint64_t num_counts_{};
};

// The sum of execution counts of all samples.
uint64_t total_count_{};
// The maximum individual count.
uint64_t max_count_{};
// The maximum head count across all functions.
uint64_t max_function_count_{};
// The number of lines that have samples.
uint64_t num_counts_{};
// The number of functions that have samples.
uint64_t num_functions_{};
// The percentile threshold information.
std::vector<DetailedSummary> detailed_summaries_{};
};

class ProfileSummaryComputer : public SymbolTraverser {
public:
// This type is neither copyable nor movable.
ProfileSummaryComputer(const ProfileSummaryComputer &) = delete;
ProfileSummaryComputer &operator=(const ProfileSummaryComputer &) = delete;

ProfileSummaryComputer();
ProfileSummaryComputer(std::vector<uint32_t> cutoffs);

static ProfileSummaryInformation Compute(const SymbolMap &symbol_map,
std::vector<uint32_t> cutoffs) {
ProfileSummaryComputer computer(std::move(cutoffs));
computer.Start(symbol_map);
computer.ComputeDetailedSummary();
return std::move(computer.info_);
}

protected:
void VisitTopSymbol(const std::string &name, const Symbol *node) override;
void Visit(const Symbol *node) override;

private:
ProfileSummaryInformation info_{};
// Sorted map of frequencies - used to compute the histogram.
std::map<uint64_t, uint32_t, std::greater<uint64_t>> count_frequencies_;
std::vector<uint32_t> cutoffs_;

void ComputeDetailedSummary();
};

} // namespace devtools_crosstool_autofdo

#endif // AUTOFDO_PROFILE_WRITER_H_