Skip to content

Commit

Permalink
[Try2][InstrProf] Add Correlator class to read debug info
Browse files Browse the repository at this point in the history
Extend `llvm-profdata` to read in a `.proflite` file and also a debug info file to generate a normal `.profdata` profile. This reduces the binary size by 8.4% when building an instrumented Clang binary without value profiling (164 MB vs 179 MB).

This work is part of the "lightweight instrumentation" RFC: https://groups.google.com/g/llvm-dev/c/r03Z6JoN7d4

This was first landed in https://reviews.llvm.org/D114566 but had to be reverted due to build errors.

Reviewed By: kyulee

Differential Revision: https://reviews.llvm.org/D115915
  • Loading branch information
ellishg committed Dec 17, 2021
1 parent 163c13f commit 65d7fd0
Show file tree
Hide file tree
Showing 12 changed files with 642 additions and 65 deletions.
31 changes: 31 additions & 0 deletions compiler-rt/test/profile/Darwin/instrprof-debug-info-correlate.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Value profiling is currently not supported in lightweight mode.
// RUN: %clang_pgogen -o %t.normal -mllvm --disable-vp=true %s
// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t.normal
// RUN: llvm-profdata merge -o %t.normal.profdata %t.profraw

// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %s
// RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t
// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t.dSYM %t.proflite

// RUN: diff %t.normal.profdata %t.profdata

int foo(int a) {
if (a % 2)
return 4 * a + 1;
return 0;
}

int bar(int a) {
while (a > 100)
a /= 2;
return a;
}

typedef int (*FP)(int);
FP Fps[3] = {foo, bar};

int main() {
for (int i = 0; i < 5; i++)
Fps[i % 2](i);
return 0;
}
31 changes: 31 additions & 0 deletions compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Value profiling is currently not supported in lightweight mode.
// RUN: %clang_pgogen -o %t.normal -mllvm --disable-vp=true %s
// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t.normal
// RUN: llvm-profdata merge -o %t.normal.profdata %t.profraw

// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %s
// RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t
// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t %t.proflite

// RUN: diff %t.normal.profdata %t.profdata

int foo(int a) {
if (a % 2)
return 4 * a + 1;
return 0;
}

int bar(int a) {
while (a > 100)
a /= 2;
return a;
}

typedef int (*FP)(int);
FP Fps[3] = {foo, bar};

int main() {
for (int i = 0; i < 5; i++)
Fps[i % 2](i);
return 0;
}
4 changes: 4 additions & 0 deletions llvm/include/llvm/ProfileData/InstrProf.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,10 @@ enum class instrprof_error {
too_large,
truncated,
malformed,
missing_debug_info_for_correlation,
unexpected_debug_info_for_correlation,
unable_to_correlate_profile,
unsupported_debug_format,
unknown_function,
invalid_prof,
hash_mismatch,
Expand Down
170 changes: 170 additions & 0 deletions llvm/include/llvm/ProfileData/InstrProfCorrelator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
//===- InstrProfCorrelator.h ------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This file defines InstrProfCorrelator used to generate PGO profiles from
// raw profile data and debug info.
//===----------------------------------------------------------------------===//

#ifndef LLVM_PROFILEDATA_INSTRPROFCORRELATOR_H
#define LLVM_PROFILEDATA_INSTRPROFCORRELATOR_H

#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h"
#include <vector>

namespace llvm {

/// InstrProfCorrelator - A base class used to create raw instrumentation data
/// to their functions.
class InstrProfCorrelator {
public:
static llvm::Expected<std::unique_ptr<InstrProfCorrelator>>
get(StringRef DebugInfoFilename);

/// Construct a ProfileData vector used to correlate raw instrumentation data
/// to their functions.
virtual Error correlateProfileData() = 0;

static const char *FunctionNameAttributeName;
static const char *CFGHashAttributeName;
static const char *NumCountersAttributeName;

enum InstrProfCorrelatorKind { CK_32Bit, CK_64Bit };
InstrProfCorrelatorKind getKind() const { return Kind; }
virtual ~InstrProfCorrelator() {}

protected:
struct Context {
static llvm::Expected<std::unique_ptr<Context>>
get(std::unique_ptr<MemoryBuffer> Buffer, const object::ObjectFile &Obj);
std::unique_ptr<MemoryBuffer> Buffer;
/// The address range of the __llvm_prf_cnts section.
uint64_t CountersSectionStart;
uint64_t CountersSectionEnd;
/// True if target and host have different endian orders.
bool ShouldSwapBytes;
};
const std::unique_ptr<InstrProfCorrelator::Context> Ctx;

InstrProfCorrelator(InstrProfCorrelatorKind K, std::unique_ptr<Context> Ctx)
: Ctx(std::move(Ctx)), Kind(K) {}

private:
static llvm::Expected<std::unique_ptr<InstrProfCorrelator>>
get(std::unique_ptr<MemoryBuffer> Buffer);

const InstrProfCorrelatorKind Kind;
};

/// InstrProfCorrelatorImpl - A child of InstrProfCorrelator with a template
/// pointer type so that the ProfileData vector can be materialized.
template <class IntPtrT>
class InstrProfCorrelatorImpl : public InstrProfCorrelator {
public:
InstrProfCorrelatorImpl(std::unique_ptr<InstrProfCorrelator::Context> Ctx);
static bool classof(const InstrProfCorrelator *C);

/// Return a pointer to the underlying ProfileData vector that this class
/// constructs.
const RawInstrProf::ProfileData<IntPtrT> *getDataPointer() const {
return Data.empty() ? nullptr : Data.data();
}

/// Return the number of ProfileData elements.
size_t getDataSize() const { return Data.size(); }

/// Return a pointer to the compressed names string that this class
/// constructs.
const char *getCompressedNamesPointer() const {
return CompressedNames.c_str();
}

/// Return the number of bytes in the compressed names string.
size_t getCompressedNamesSize() const { return CompressedNames.size(); }

static llvm::Expected<std::unique_ptr<InstrProfCorrelatorImpl<IntPtrT>>>
get(std::unique_ptr<InstrProfCorrelator::Context> Ctx,
const object::ObjectFile &Obj);

protected:
std::vector<RawInstrProf::ProfileData<IntPtrT>> Data;
std::string CompressedNames;

Error correlateProfileData() override;
virtual void correlateProfileDataImpl() = 0;

void addProbe(StringRef FunctionName, uint64_t CFGHash, IntPtrT CounterOffset,
IntPtrT FunctionPtr, uint32_t NumCounters);

private:
InstrProfCorrelatorImpl(InstrProfCorrelatorKind Kind,
std::unique_ptr<InstrProfCorrelator::Context> Ctx)
: InstrProfCorrelator(Kind, std::move(Ctx)){};
std::vector<std::string> Names;

// Byte-swap the value if necessary.
template <class T> T maybeSwap(T Value) const {
return Ctx->ShouldSwapBytes ? sys::getSwappedBytes(Value) : Value;
}
};

/// DwarfInstrProfCorrelator - A child of InstrProfCorrelatorImpl that takes
/// DWARF debug info as input to correlate profiles.
template <class IntPtrT>
class DwarfInstrProfCorrelator : public InstrProfCorrelatorImpl<IntPtrT> {
public:
DwarfInstrProfCorrelator(std::unique_ptr<DWARFContext> DICtx,
std::unique_ptr<InstrProfCorrelator::Context> Ctx)
: InstrProfCorrelatorImpl<IntPtrT>(std::move(Ctx)),
DICtx(std::move(DICtx)) {}

private:
std::unique_ptr<DWARFContext> DICtx;

/// Return the address of the object that the provided DIE symbolizes.
llvm::Optional<uint64_t> getLocation(const DWARFDie &Die) const;

/// Returns true if the provided DIE symbolizes an instrumentation probe
/// symbol.
static bool isDIEOfProbe(const DWARFDie &Die);

/// Iterate over DWARF DIEs to find those that symbolize instrumentation
/// probes and construct the ProfileData vector and CompressedNames string.
///
/// Here is some example DWARF for an instrumentation probe we are looking
/// for:
/// \code
/// DW_TAG_subprogram
/// DW_AT_low_pc (0x0000000000000000)
/// DW_AT_high_pc (0x0000000000000014)
/// DW_AT_name ("foo")
/// DW_TAG_variable
/// DW_AT_name ("__profc_foo")
/// DW_AT_location (DW_OP_addr 0x0)
/// DW_TAG_LLVM_annotation
/// DW_AT_name ("Function Name")
/// DW_AT_const_value ("foo")
/// DW_TAG_LLVM_annotation
/// DW_AT_name ("CFG Hash")
/// DW_AT_const_value (12345678)
/// DW_TAG_LLVM_annotation
/// DW_AT_name ("Num Counters")
/// DW_AT_const_value (2)
/// NULL
/// NULL
/// \endcode
void correlateProfileDataImpl() override;
};

} // end namespace llvm

#endif // LLVM_PROFILEDATA_INSTRPROFCORRELATOR_H
26 changes: 21 additions & 5 deletions llvm/include/llvm/ProfileData/InstrProfReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/ProfileSummary.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/ProfileData/InstrProfCorrelator.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/LineIterator.h"
Expand Down Expand Up @@ -96,6 +97,9 @@ class InstrProfReader {

virtual bool instrEntryBBEnabled() const = 0;

/// Return true if we must provide debug info to create PGO profiles.
virtual bool useDebugInfoCorrelate() const { return false; }

/// Return the PGO symtab. There are three different readers:
/// Raw, Text, and Indexed profile readers. The first two types
/// of readers are used only by llvm-profdata tool, while the indexed
Expand Down Expand Up @@ -150,10 +154,12 @@ class InstrProfReader {

/// Factory method to create an appropriately typed reader for the given
/// instrprof file.
static Expected<std::unique_ptr<InstrProfReader>> create(const Twine &Path);
static Expected<std::unique_ptr<InstrProfReader>>
create(const Twine &Path, const InstrProfCorrelator *Correlator = nullptr);

static Expected<std::unique_ptr<InstrProfReader>>
create(std::unique_ptr<MemoryBuffer> Buffer);
create(std::unique_ptr<MemoryBuffer> Buffer,
const InstrProfCorrelator *Correlator = nullptr);
};

/// Reader for the simple text based instrprof format.
Expand Down Expand Up @@ -215,6 +221,9 @@ class RawInstrProfReader : public InstrProfReader {
private:
/// The profile data file contents.
std::unique_ptr<MemoryBuffer> DataBuffer;
/// If available, this hold the ProfileData array used to correlate raw
/// instrumentation data to their functions.
const InstrProfCorrelatorImpl<IntPtrT> *Correlator;
bool ShouldSwapBytes;
// The value of the version field of the raw profile data header. The lower 56
// bits specifies the format version and the most significant 8 bits specify
Expand All @@ -226,7 +235,7 @@ class RawInstrProfReader : public InstrProfReader {
const RawInstrProf::ProfileData<IntPtrT> *DataEnd;
const uint64_t *CountersStart;
const char *NamesStart;
uint64_t NamesSize;
const char *NamesEnd;
// After value profile is all read, this pointer points to
// the header of next profile data (if exists)
const uint8_t *ValueDataStart;
Expand All @@ -237,8 +246,11 @@ class RawInstrProfReader : public InstrProfReader {
const uint8_t *BinaryIdsStart;

public:
RawInstrProfReader(std::unique_ptr<MemoryBuffer> DataBuffer)
: DataBuffer(std::move(DataBuffer)) {}
RawInstrProfReader(std::unique_ptr<MemoryBuffer> DataBuffer,
const InstrProfCorrelator *Correlator)
: DataBuffer(std::move(DataBuffer)),
Correlator(dyn_cast_or_null<const InstrProfCorrelatorImpl<IntPtrT>>(
Correlator)) {}
RawInstrProfReader(const RawInstrProfReader &) = delete;
RawInstrProfReader &operator=(const RawInstrProfReader &) = delete;

Expand All @@ -259,6 +271,10 @@ class RawInstrProfReader : public InstrProfReader {
return (Version & VARIANT_MASK_INSTR_ENTRY) != 0;
}

bool useDebugInfoCorrelate() const override {
return (Version & VARIANT_MASK_DBG_CORRELATE) != 0;
}

InstrProfSymtab &getSymtab() override {
assert(Symtab.get());
return *Symtab.get();
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/ProfileData/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
add_llvm_component_library(LLVMProfileData
GCOV.cpp
InstrProf.cpp
InstrProfCorrelator.cpp
InstrProfReader.cpp
InstrProfWriter.cpp
ProfileSummaryBuilder.cpp
Expand All @@ -19,6 +20,8 @@ add_llvm_component_library(LLVMProfileData
Core
Support
Demangle
Object
DebugInfoDWARF
)

add_subdirectory(Coverage)
12 changes: 12 additions & 0 deletions llvm/lib/ProfileData/InstrProf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,18 @@ static std::string getInstrProfErrString(instrprof_error Err,
case instrprof_error::malformed:
OS << "malformed instrumentation profile data";
break;
case instrprof_error::missing_debug_info_for_correlation:
OS << "debug info for correlation is required";
break;
case instrprof_error::unexpected_debug_info_for_correlation:
OS << "debug info for correlation is not necessary";
break;
case instrprof_error::unable_to_correlate_profile:
OS << "unable to correlate profile";
break;
case instrprof_error::unsupported_debug_format:
OS << "unsupported debug info format (only DWARF is supported)";
break;
case instrprof_error::invalid_prof:
OS << "invalid profile created. Please file a bug "
"at: " BUG_REPORT_URL
Expand Down
Loading

0 comments on commit 65d7fd0

Please sign in to comment.