SYNOPSIS
^^^^^^^^
-:program:`llvm-profdata merge` [*options*] [*filenames...*]
+:program:`llvm-profdata merge` [*options*] [*filename...*]
DESCRIPTION
^^^^^^^^^^^
generated by PGO instrumentation and merges them together into a single
indexed profile data file.
+By default profile data is merged without modification. This means that the
+relative importance of each input file is proportional to the number of samples
+or counts it contains. In general, the input from a longer training run will be
+interpreted as relatively more important than a shorter run. Depending on the
+nature of the training runs it may be useful to adjust the weight given to each
+input file by using the ``-weighted-input`` option.
+
+
OPTIONS
^^^^^^^
Specify the output file name. *Output* cannot be ``-`` as the resulting
indexed profile data can't be written to standard output.
+.. option:: -weighted-input=weight,filename
+
+ Specify an input file name along with a weight. The profile counts of the input
+ file will be scaled (multiplied) by the supplied ``weight``, where where ``weight``
+ is a decimal integer >= 1. Input files specified without using this option are
+ assigned a default weight of 1. Examples are shown below.
+
.. option:: -instr (default)
Specify that the input profile is an instrumentation-based profile.
Emit the profile using GCC's gcov format (Not yet supported).
+EXAMPLES
+^^^^^^^^
+Basic Usage
++++++++++++
+Merge three profiles:
+
+::
+
+ llvm-profdata merge foo.profdata bar.profdata baz.profdata -output merged.profdata
+
+Weighted Input
+++++++++++++++
+The input file `foo.profdata` is especially important, multiply its counts by 10:
+
+::
+
+ llvm-profdata merge -weighted-input=10,foo.profdata bar.profdata baz.profdata -output merged.profdata
+
+Exactly equivalent to the previous invocation (explicit form; useful for programmatic invocation):
+
+::
+
+ llvm-profdata merge -weighted-input=10,foo.profdata -weighted-input=1,bar.profdata -weighted-input=1,baz.profdata -output merged.profdata
+
.. program:: llvm-profdata show
.. _profdata-show:
}
/// Merge data from another InstrProfValueSiteRecord
- void mergeValueData(InstrProfValueSiteRecord &Input) {
+ /// Optionally scale merged counts by \p Weight.
+ void mergeValueData(InstrProfValueSiteRecord &Input, uint64_t Weight = 1) {
this->sortByTargetValues();
Input.sortByTargetValues();
auto I = ValueData.begin();
while (I != IE && I->Value < J->Value)
++I;
if (I != IE && I->Value == J->Value) {
- I->Count = SaturatingAdd(I->Count, J->Count);
+ // FIXME: Improve handling of counter overflow.
+ uint64_t JCount = J->Count;
+ bool Overflowed;
+ if (Weight > 1) {
+ JCount = SaturatingMultiply(JCount, Weight, &Overflowed);
+ assert(!Overflowed && "Value data counter overflowed!");
+ }
+ I->Count = SaturatingAdd(I->Count, JCount, &Overflowed);
+ assert(!Overflowed && "Value data counter overflowed!");
++I;
continue;
}
ValueMapType *HashKeys);
/// Merge the counts in \p Other into this one.
- inline instrprof_error merge(InstrProfRecord &Other);
+ /// Optionally scale merged counts by \p Weight.
+ inline instrprof_error merge(InstrProfRecord &Other, uint64_t Weight = 1);
/// Used by InstrProfWriter: update the value strings to commoned strings in
/// the writer instance.
}
// Merge Value Profile data from Src record to this record for ValueKind.
- instrprof_error mergeValueProfData(uint32_t ValueKind, InstrProfRecord &Src) {
+ // Scale merged value counts by \p Weight.
+ instrprof_error mergeValueProfData(uint32_t ValueKind, InstrProfRecord &Src,
+ uint64_t Weight) {
uint32_t ThisNumValueSites = getNumValueSites(ValueKind);
uint32_t OtherNumValueSites = Src.getNumValueSites(ValueKind);
if (ThisNumValueSites != OtherNumValueSites)
std::vector<InstrProfValueSiteRecord> &OtherSiteRecords =
Src.getValueSitesForKind(ValueKind);
for (uint32_t I = 0; I < ThisNumValueSites; I++)
- ThisSiteRecords[I].mergeValueData(OtherSiteRecords[I]);
+ ThisSiteRecords[I].mergeValueData(OtherSiteRecords[I], Weight);
return instrprof_error::success;
}
};
VData.Value = (uint64_t)StrTab->insertString((const char *)VData.Value);
}
-instrprof_error InstrProfRecord::merge(InstrProfRecord &Other) {
+instrprof_error InstrProfRecord::merge(InstrProfRecord &Other,
+ uint64_t Weight) {
// If the number of counters doesn't match we either have bad data
// or a hash collision.
if (Counts.size() != Other.Counts.size())
instrprof_error Result = instrprof_error::success;
for (size_t I = 0, E = Other.Counts.size(); I < E; ++I) {
- bool ResultOverflowed;
- Counts[I] = SaturatingAdd(Counts[I], Other.Counts[I], &ResultOverflowed);
- if (ResultOverflowed)
+ bool Overflowed;
+ uint64_t OtherCount = Other.Counts[I];
+ if (Weight > 1) {
+ OtherCount = SaturatingMultiply(OtherCount, Weight, &Overflowed);
+ if (Overflowed)
+ Result = instrprof_error::counter_overflow;
+ }
+ Counts[I] = SaturatingAdd(Counts[I], OtherCount, &Overflowed);
+ if (Overflowed)
Result = instrprof_error::counter_overflow;
}
for (uint32_t Kind = IPVK_First; Kind <= IPVK_Last; ++Kind) {
- instrprof_error MergeValueResult = mergeValueProfData(Kind, Other);
+ instrprof_error MergeValueResult = mergeValueProfData(Kind, Other, Weight);
if (MergeValueResult != instrprof_error::success)
Result = MergeValueResult;
}
void updateStringTableReferences(InstrProfRecord &I);
/// Add function counts for the given function. If there are already counts
/// for this function and the hash and number of counts match, each counter is
- /// summed.
- std::error_code addRecord(InstrProfRecord &&I);
+ /// summed. Optionally scale counts by \p Weight.
+ std::error_code addRecord(InstrProfRecord &&I, uint64_t Weight = 1);
/// Write the profile to \c OS
void write(raw_fd_ostream &OS);
/// Write the profile in text format to \c OS
SampleRecord() : NumSamples(0), CallTargets() {}
/// Increment the number of samples for this record by \p S.
+ /// Optionally scale sample count \p S by \p Weight.
///
/// Sample counts accumulate using saturating arithmetic, to avoid wrapping
/// around unsigned integers.
- void addSamples(uint64_t S) { NumSamples = SaturatingAdd(NumSamples, S); }
+ void addSamples(uint64_t S, uint64_t Weight = 1) {
+ // FIXME: Improve handling of counter overflow.
+ bool Overflowed;
+ if (Weight > 1) {
+ S = SaturatingMultiply(S, Weight, &Overflowed);
+ assert(!Overflowed && "Sample counter overflowed!");
+ }
+ NumSamples = SaturatingAdd(NumSamples, S, &Overflowed);
+ assert(!Overflowed && "Sample counter overflowed!");
+ }
/// Add called function \p F with samples \p S.
+ /// Optionally scale sample count \p S by \p Weight.
///
/// Sample counts accumulate using saturating arithmetic, to avoid wrapping
/// around unsigned integers.
- void addCalledTarget(StringRef F, uint64_t S) {
+ void addCalledTarget(StringRef F, uint64_t S, uint64_t Weight = 1) {
+ // FIXME: Improve handling of counter overflow.
uint64_t &TargetSamples = CallTargets[F];
- TargetSamples = SaturatingAdd(TargetSamples, S);
+ bool Overflowed;
+ if (Weight > 1) {
+ S = SaturatingMultiply(S, Weight, &Overflowed);
+ assert(!Overflowed && "Called target counter overflowed!");
+ }
+ TargetSamples = SaturatingAdd(TargetSamples, S, &Overflowed);
+ assert(!Overflowed && "Called target counter overflowed!");
}
/// Return true if this sample record contains function calls.
const CallTargetMap &getCallTargets() const { return CallTargets; }
/// Merge the samples in \p Other into this record.
- void merge(const SampleRecord &Other) {
- addSamples(Other.getSamples());
+ /// Optionally scale sample counts by \p Weight.
+ void merge(const SampleRecord &Other, uint64_t Weight = 1) {
+ addSamples(Other.getSamples(), Weight);
for (const auto &I : Other.getCallTargets())
- addCalledTarget(I.first(), I.second);
+ addCalledTarget(I.first(), I.second, Weight);
}
void print(raw_ostream &OS, unsigned Indent) const;
FunctionSamples() : TotalSamples(0), TotalHeadSamples(0) {}
void print(raw_ostream &OS = dbgs(), unsigned Indent = 0) const;
void dump() const;
- void addTotalSamples(uint64_t Num) { TotalSamples += Num; }
- void addHeadSamples(uint64_t Num) { TotalHeadSamples += Num; }
- void addBodySamples(uint32_t LineOffset, uint32_t Discriminator,
- uint64_t Num) {
- BodySamples[LineLocation(LineOffset, Discriminator)].addSamples(Num);
+ void addTotalSamples(uint64_t Num, uint64_t Weight = 1) {
+ // FIXME: Improve handling of counter overflow.
+ bool Overflowed;
+ if (Weight > 1) {
+ Num = SaturatingMultiply(Num, Weight, &Overflowed);
+ assert(!Overflowed && "Total samples counter overflowed!");
+ }
+ TotalSamples = SaturatingAdd(TotalSamples, Num, &Overflowed);
+ assert(!Overflowed && "Total samples counter overflowed!");
+ }
+ void addHeadSamples(uint64_t Num, uint64_t Weight = 1) {
+ // FIXME: Improve handling of counter overflow.
+ bool Overflowed;
+ if (Weight > 1) {
+ Num = SaturatingMultiply(Num, Weight, &Overflowed);
+ assert(!Overflowed && "Total head samples counter overflowed!");
+ }
+ TotalHeadSamples = SaturatingAdd(TotalHeadSamples, Num, &Overflowed);
+ assert(!Overflowed && "Total head samples counter overflowed!");
+ }
+ void addBodySamples(uint32_t LineOffset, uint32_t Discriminator, uint64_t Num,
+ uint64_t Weight = 1) {
+ BodySamples[LineLocation(LineOffset, Discriminator)].addSamples(Num,
+ Weight);
}
void addCalledTargetSamples(uint32_t LineOffset, uint32_t Discriminator,
- std::string FName, uint64_t Num) {
- BodySamples[LineLocation(LineOffset, Discriminator)].addCalledTarget(FName,
- Num);
+ std::string FName, uint64_t Num,
+ uint64_t Weight = 1) {
+ BodySamples[LineLocation(LineOffset, Discriminator)].addCalledTarget(
+ FName, Num, Weight);
}
/// Return the number of samples collected at the given location.
}
/// Merge the samples in \p Other into this one.
- void merge(const FunctionSamples &Other) {
- addTotalSamples(Other.getTotalSamples());
- addHeadSamples(Other.getHeadSamples());
+ /// Optionally scale samples by \p Weight.
+ void merge(const FunctionSamples &Other, uint64_t Weight = 1) {
+ addTotalSamples(Other.getTotalSamples(), Weight);
+ addHeadSamples(Other.getHeadSamples(), Weight);
for (const auto &I : Other.getBodySamples()) {
const LineLocation &Loc = I.first;
const SampleRecord &Rec = I.second;
- BodySamples[Loc].merge(Rec);
+ BodySamples[Loc].merge(Rec, Weight);
}
for (const auto &I : Other.getCallsiteSamples()) {
const CallsiteLocation &Loc = I.first;
const FunctionSamples &Rec = I.second;
- functionSamplesAt(Loc).merge(Rec);
+ functionSamplesAt(Loc).merge(Rec, Weight);
}
}
I.updateStrings(&StringTable);
}
-std::error_code InstrProfWriter::addRecord(InstrProfRecord &&I) {
+std::error_code InstrProfWriter::addRecord(InstrProfRecord &&I,
+ uint64_t Weight) {
updateStringTableReferences(I);
auto &ProfileDataMap = FunctionData[I.Name];
// We've never seen a function with this name and hash, add it.
Dest = std::move(I);
Result = instrprof_error::success;
+ if (Weight > 1) {
+ for (auto &Count : Dest.Counts) {
+ bool Overflowed;
+ Count = SaturatingMultiply(Count, Weight, &Overflowed);
+ if (Overflowed && Result == instrprof_error::success) {
+ Result = instrprof_error::counter_overflow;
+ }
+ }
+ }
} else {
// We're updating a function we've seen before.
- Result = Dest.merge(I);
+ Result = Dest.merge(I, Weight);
}
// We keep track of the max function count as we go for simplicity.
--- /dev/null
+bar:1772037:35370
+ 17: 35370
+ 18: 35370
+ 19: 7005
+ 20: 29407
+ 21: 12170
+ 23: 18150 bar:19829
+ 25: 36666
--- /dev/null
+foo:1763288:35327
+ 7: 35327
+ 8: 35327
+ 9: 6930
+ 10: 29341
+ 11: 11906
+ 13: 18185 foo:19531
+ 15: 36458
1- Detect invalid count
RUN: not llvm-profdata show %p/Inputs/invalid-count-later.proftext 2>&1 | FileCheck %s --check-prefix=INVALID-COUNT-LATER
-RUN: not llvm-profdata merge %p/Inputs/invalid-count-later.proftext %p/Inputs/invalid-count-later.profdata -o %t.out 2>&1 | FileCheck %s --check-prefix=INVALID-COUNT-LATER
+RUN: not llvm-profdata merge %p/Inputs/invalid-count-later.proftext %p/Inputs/invalid-count-later.proftext -o %t.out 2>&1 | FileCheck %s --check-prefix=INVALID-COUNT-LATER
INVALID-COUNT-LATER: error: {{.*}}invalid-count-later.proftext: Malformed instrumentation profile data
2- Detect bad hash
--- /dev/null
+Tests for weighted merge of instrumented profiles.
+
+1- Merge the foo and bar profiles with unity weight and verify the combined output
+RUN: llvm-profdata merge -instr -weighted-input=1,%p/Inputs/weight-instr-bar.profdata -weighted-input=1,%p/Inputs/weight-instr-foo.profdata -o %t
+RUN: llvm-profdata show -instr -all-functions %t | FileCheck %s -check-prefix=1X_1X_WEIGHT
+RUN: llvm-profdata merge -instr -weighted-input=1,%p/Inputs/weight-instr-bar.profdata %p/Inputs/weight-instr-foo.profdata -o %t
+RUN: llvm-profdata show -instr -all-functions %t | FileCheck %s -check-prefix=1X_1X_WEIGHT
+1X_1X_WEIGHT: Counters:
+1X_1X_WEIGHT-NEXT: usage:
+1X_1X_WEIGHT-NEXT: Hash: 0x0000000000000000
+1X_1X_WEIGHT-NEXT: Counters: 1
+1X_1X_WEIGHT-NEXT: Function count: 0
+1X_1X_WEIGHT-NEXT: foo:
+1X_1X_WEIGHT-NEXT: Hash: 0x000000000000028a
+1X_1X_WEIGHT-NEXT: Counters: 3
+1X_1X_WEIGHT-NEXT: Function count: 866988873
+1X_1X_WEIGHT-NEXT: bar:
+1X_1X_WEIGHT-NEXT: Hash: 0x000000000000028a
+1X_1X_WEIGHT-NEXT: Counters: 3
+1X_1X_WEIGHT-NEXT: Function count: 866988873
+1X_1X_WEIGHT-NEXT: main:
+1X_1X_WEIGHT-NEXT: Hash: 0x7d31c47ea98f8248
+1X_1X_WEIGHT-NEXT: Counters: 60
+1X_1X_WEIGHT-NEXT: Function count: 2
+1X_1X_WEIGHT-NEXT: Functions shown: 4
+1X_1X_WEIGHT-NEXT: Total functions: 4
+1X_1X_WEIGHT-NEXT: Maximum function count: 866988873
+1X_1X_WEIGHT-NEXT: Maximum internal block count: 267914296
+
+2- Merge the foo and bar profiles with weight 3x and 5x respectively and verify the combined output
+RUN: llvm-profdata merge -instr -weighted-input=3,%p/Inputs/weight-instr-bar.profdata -weighted-input=5,%p/Inputs/weight-instr-foo.profdata -o %t
+RUN: llvm-profdata show -instr -all-functions %t | FileCheck %s -check-prefix=3X_5X_WEIGHT
+3X_5X_WEIGHT: Counters:
+3X_5X_WEIGHT-NEXT: usage:
+3X_5X_WEIGHT-NEXT: Hash: 0x0000000000000000
+3X_5X_WEIGHT-NEXT: Counters: 1
+3X_5X_WEIGHT-NEXT: Function count: 0
+3X_5X_WEIGHT-NEXT: foo:
+3X_5X_WEIGHT-NEXT: Hash: 0x000000000000028a
+3X_5X_WEIGHT-NEXT: Counters: 3
+3X_5X_WEIGHT-NEXT: Function count: 4334944365
+3X_5X_WEIGHT-NEXT: bar:
+3X_5X_WEIGHT-NEXT: Hash: 0x000000000000028a
+3X_5X_WEIGHT-NEXT: Counters: 3
+3X_5X_WEIGHT-NEXT: Function count: 2600966619
+3X_5X_WEIGHT-NEXT: main:
+3X_5X_WEIGHT-NEXT: Hash: 0x7d31c47ea98f8248
+3X_5X_WEIGHT-NEXT: Counters: 60
+3X_5X_WEIGHT-NEXT: Function count: 8
+3X_5X_WEIGHT-NEXT: Functions shown: 4
+3X_5X_WEIGHT-NEXT: Total functions: 4
+3X_5X_WEIGHT-NEXT: Maximum function count: 4334944365
+3X_5X_WEIGHT-NEXT: Maximum internal block count: 1339571480
+
+3- Bad merge: invalid weight
+RUN: not llvm-profdata merge -instr -weighted-input=3,%p/Inputs/weight-instr-bar.profdata -weighted-input=0,%p/Inputs/weight-instr-foo.profdata -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT
+RUN: not llvm-profdata merge -instr -weighted-input=3,%p/Inputs/weight-instr-bar.profdata -weighted-input=0.75,%p/Inputs/weight-instr-foo.profdata -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT
+RUN: not llvm-profdata merge -instr -weighted-input=3,%p/Inputs/weight-instr-bar.profdata -weighted-input=-5,%p/Inputs/weight-instr-foo.profdata -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT
+RUN: not llvm-profdata merge -instr -weighted-input=3,%p/Inputs/weight-instr-bar.profdata -weighted-input=,%p/Inputs/weight-instr-foo.profdata -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT
+RUN: not llvm-profdata merge -instr -weighted-input=3,%p/Inputs/weight-instr-bar.profdata -weighted-input=%p/Inputs/weight-instr-foo.profdata -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT
+INVALID_WEIGHT: error: Input weight must be a positive integer.
+
+4- Bad merge: input path does not exist
+RUN: not llvm-profdata merge -instr -weighted-input=3,%p/Inputs/does-not-exist.profdata -weighted-input=2,%p/Inputs/does-not-exist-either.profdata -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_INPUT
+INVALID_INPUT: {{.*}}: {{.*}}does-not-exist.profdata: {{[Nn]}}o such file or directory
+
+5- No inputs
+RUN: not llvm-profdata merge -instr -o %t.out 2>&1 | FileCheck %s -check-prefix=NO_INPUT
+NO_INPUT: {{.*}}: No input files specified. See llvm-profdata{{(\.EXE|\.exe)?}} merge -help
--- /dev/null
+Tests for weighted merge of sample profiles.
+
+1- Merge the foo and bar profiles with unity weight and verify the combined output
+RUN: llvm-profdata merge -sample -text -weighted-input=1,%p/Inputs/weight-sample-bar.proftext -weighted-input=1,%p/Inputs/weight-sample-foo.proftext -o - | FileCheck %s -check-prefix=1X_1X_WEIGHT
+RUN: llvm-profdata merge -sample -text -weighted-input=1,%p/Inputs/weight-sample-bar.proftext %p/Inputs/weight-sample-foo.proftext -o - | FileCheck %s -check-prefix=1X_1X_WEIGHT
+1X_1X_WEIGHT: foo:1763288:35327
+1X_1X_WEIGHT-NEXT: 7: 35327
+1X_1X_WEIGHT-NEXT: 8: 35327
+1X_1X_WEIGHT-NEXT: 9: 6930
+1X_1X_WEIGHT-NEXT: 10: 29341
+1X_1X_WEIGHT-NEXT: 11: 11906
+1X_1X_WEIGHT-NEXT: 13: 18185 foo:19531
+1X_1X_WEIGHT-NEXT: 15: 36458
+1X_1X_WEIGHT-NEXT: bar:1772037:35370
+1X_1X_WEIGHT-NEXT: 17: 35370
+1X_1X_WEIGHT-NEXT: 18: 35370
+1X_1X_WEIGHT-NEXT: 19: 7005
+1X_1X_WEIGHT-NEXT: 20: 29407
+1X_1X_WEIGHT-NEXT: 21: 12170
+1X_1X_WEIGHT-NEXT: 23: 18150 bar:19829
+1X_1X_WEIGHT-NEXT: 25: 36666
+
+2- Merge the foo and bar profiles with weight 3x and 5x respectively and verify the combined output
+RUN: llvm-profdata merge -sample -text -weighted-input=3,%p/Inputs/weight-sample-bar.proftext -weighted-input=5,%p/Inputs/weight-sample-foo.proftext -o - | FileCheck %s -check-prefix=3X_5X_WEIGHT
+3X_5X_WEIGHT: foo:8816440:176635
+3X_5X_WEIGHT-NEXT: 7: 176635
+3X_5X_WEIGHT-NEXT: 8: 176635
+3X_5X_WEIGHT-NEXT: 9: 34650
+3X_5X_WEIGHT-NEXT: 10: 146705
+3X_5X_WEIGHT-NEXT: 11: 59530
+3X_5X_WEIGHT-NEXT: 13: 90925 foo:97655
+3X_5X_WEIGHT-NEXT: 15: 182290
+3X_5X_WEIGHT-NEXT: bar:5316111:106110
+3X_5X_WEIGHT-NEXT: 17: 106110
+3X_5X_WEIGHT-NEXT: 18: 106110
+3X_5X_WEIGHT-NEXT: 19: 21015
+3X_5X_WEIGHT-NEXT: 20: 88221
+3X_5X_WEIGHT-NEXT: 21: 36510
+3X_5X_WEIGHT-NEXT: 23: 54450 bar:59487
+3X_5X_WEIGHT-NEXT: 25: 109998
+
+3- Bad merge: invalid weight
+RUN: not llvm-profdata merge -sample -weighted-input=3,%p/Inputs/weight-sample-bar.proftext -weighted-input=0,%p/Inputs/weight-sample-foo.proftext -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT
+RUN: not llvm-profdata merge -sample -weighted-input=3,%p/Inputs/weight-sample-bar.proftext -weighted-input=0.75,%p/Inputs/weight-sample-foo.proftext -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT
+RUN: not llvm-profdata merge -sample -weighted-input=3,%p/Inputs/weight-sample-bar.proftext -weighted-input=-5,%p/Inputs/weight-sample-foo.proftext -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT
+RUN: not llvm-profdata merge -sample -weighted-input=3,%p/Inputs/weight-sample-bar.proftext -weighted-input=,%p/Inputs/weight-sample-foo.proftext -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT
+RUN: not llvm-profdata merge -sample -weighted-input=3,%p/Inputs/weight-sample-bar.proftext -weighted-input=%p/Inputs/weight-sample-foo.proftext -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT
+INVALID_WEIGHT: error: Input weight must be a positive integer.
+
+4- Bad merge: input path does not exist
+RUN: not llvm-profdata merge -sample -weighted-input=3,%p/Inputs/does-not-exist.proftext -weighted-input=2,%p/Inputs/does-not-exist-either.proftext -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_INPUT
+INVALID_INPUT: {{.*}}: {{.*}}does-not-exist.proftext: {{[Nn]}}o such file or directory
+
+5- No inputs
+RUN: not llvm-profdata merge -sample -o %t.out 2>&1 | FileCheck %s -check-prefix=NO_INPUT
+NO_INPUT: {{.*}}: No input files specified. See llvm-profdata{{(\.EXE|\.exe)?}} merge -help
//===----------------------------------------------------------------------===//
#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/ProfileData/SampleProfReader.h"
#include "llvm/ProfileData/SampleProfWriter.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <tuple>
using namespace llvm;
}
}
-static void mergeInstrProfile(const cl::list<std::string> &Inputs,
+struct WeightedFile {
+ StringRef Filename;
+ uint64_t Weight;
+
+ WeightedFile() {}
+
+ WeightedFile(StringRef F, uint64_t W) : Filename{F}, Weight{W} {}
+};
+typedef SmallVector<WeightedFile, 5> WeightedFileVector;
+
+static void mergeInstrProfile(const WeightedFileVector &Inputs,
StringRef OutputFilename,
ProfileFormat OutputFormat) {
if (OutputFilename.compare("-") == 0)
InstrProfWriter Writer;
SmallSet<std::error_code, 4> WriterErrorCodes;
- for (const auto &Filename : Inputs) {
- auto ReaderOrErr = InstrProfReader::create(Filename);
+ for (const auto &Input : Inputs) {
+ auto ReaderOrErr = InstrProfReader::create(Input.Filename);
if (std::error_code ec = ReaderOrErr.getError())
- exitWithErrorCode(ec, Filename);
+ exitWithErrorCode(ec, Input.Filename);
auto Reader = std::move(ReaderOrErr.get());
for (auto &I : *Reader) {
- if (std::error_code EC = Writer.addRecord(std::move(I))) {
+ if (std::error_code EC = Writer.addRecord(std::move(I), Input.Weight)) {
// Only show hint the first time an error occurs.
bool firstTime = WriterErrorCodes.insert(EC).second;
- handleMergeWriterError(EC, Filename, I.Name, firstTime);
+ handleMergeWriterError(EC, Input.Filename, I.Name, firstTime);
}
}
if (Reader->hasError())
- exitWithErrorCode(Reader->getError(), Filename);
+ exitWithErrorCode(Reader->getError(), Input.Filename);
}
if (OutputFormat == PF_Text)
Writer.writeText(Output);
sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Binary,
sampleprof::SPF_GCC};
-static void mergeSampleProfile(const cl::list<std::string> &Inputs,
+static void mergeSampleProfile(const WeightedFileVector &Inputs,
StringRef OutputFilename,
ProfileFormat OutputFormat) {
using namespace sampleprof;
auto Writer = std::move(WriterOrErr.get());
StringMap<FunctionSamples> ProfileMap;
SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
- for (const auto &Filename : Inputs) {
+ for (const auto &Input : Inputs) {
auto ReaderOrErr =
- SampleProfileReader::create(Filename, getGlobalContext());
+ SampleProfileReader::create(Input.Filename, getGlobalContext());
if (std::error_code EC = ReaderOrErr.getError())
- exitWithErrorCode(EC, Filename);
+ exitWithErrorCode(EC, Input.Filename);
// We need to keep the readers around until after all the files are
// read so that we do not lose the function names stored in each
Readers.push_back(std::move(ReaderOrErr.get()));
const auto Reader = Readers.back().get();
if (std::error_code EC = Reader->read())
- exitWithErrorCode(EC, Filename);
+ exitWithErrorCode(EC, Input.Filename);
StringMap<FunctionSamples> &Profiles = Reader->getProfiles();
for (StringMap<FunctionSamples>::iterator I = Profiles.begin(),
I != E; ++I) {
StringRef FName = I->first();
FunctionSamples &Samples = I->second;
- ProfileMap[FName].merge(Samples);
+ ProfileMap[FName].merge(Samples, Input.Weight);
}
}
Writer->write(ProfileMap);
}
-static int merge_main(int argc, const char *argv[]) {
- cl::list<std::string> Inputs(cl::Positional, cl::Required, cl::OneOrMore,
- cl::desc("<filenames...>"));
+static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
+ StringRef WeightStr, FileName;
+ std::tie(WeightStr, FileName) = WeightedFilename.split(',');
+
+ uint64_t Weight;
+ if (WeightStr.getAsInteger(10, Weight) || Weight < 1)
+ exitWithError("Input weight must be a positive integer.");
+
+ if (!sys::fs::exists(FileName))
+ exitWithErrorCode(make_error_code(errc::no_such_file_or_directory),
+ FileName);
+ return WeightedFile(FileName, Weight);
+}
+
+static int merge_main(int argc, const char *argv[]) {
+ cl::list<std::string> InputFilenames(cl::Positional,
+ cl::desc("<filename...>"));
+ cl::list<std::string> WeightedInputFilenames("weighted-input",
+ cl::desc("<weight>,<filename>"));
cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
cl::init("-"), cl::Required,
cl::desc("Output file"));
cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
+ if (InputFilenames.empty() && WeightedInputFilenames.empty())
+ exitWithError("No input files specified. See " +
+ sys::path::filename(argv[0]) + " -help");
+
+ WeightedFileVector WeightedInputs;
+ for (StringRef Filename : InputFilenames)
+ WeightedInputs.push_back(WeightedFile(Filename, 1));
+ for (StringRef WeightedFilename : WeightedInputFilenames)
+ WeightedInputs.push_back(parseWeightedFile(WeightedFilename));
+
if (ProfileKind == instr)
- mergeInstrProfile(Inputs, OutputFilename, OutputFormat);
+ mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat);
else
- mergeSampleProfile(Inputs, OutputFilename, OutputFormat);
+ mergeSampleProfile(WeightedInputs, OutputFilename, OutputFormat);
return 0;
}
Record1.addValueData(IPVK_IndirectCallTarget, 0, VD1, 1, nullptr);
Record2.reserveSites(IPVK_IndirectCallTarget, 1);
- InstrProfValueData VD2[] = {{(uint64_t) "callee1", Max}};
+ // FIXME: Improve handling of counter overflow. ValueData asserts on overflow.
+ // InstrProfValueData VD2[] = {{(uint64_t) "callee1", Max}};
+ InstrProfValueData VD2[] = {{(uint64_t) "callee1", 1}};
Record2.addValueData(IPVK_IndirectCallTarget, 0, VD2, 1, nullptr);
Writer.addRecord(std::move(Record1));
std::unique_ptr<InstrProfValueData[]> VD =
R.get().getValueForSite(IPVK_IndirectCallTarget, 0);
ASSERT_EQ(StringRef("callee1"), StringRef((const char *)VD[0].Value, 7));
- ASSERT_EQ(Max, VD[0].Count);
+
+ // FIXME: Improve handling of counter overflow. ValueData asserts on overflow.
+ // ASSERT_EQ(Max, VD[0].Count);
+ ASSERT_EQ(2U, VD[0].Count);
}
// Synthesize runtime value profile data.
ASSERT_EQ(1ULL << 63, Reader->getMaximumFunctionCount());
}
+TEST_F(InstrProfTest, get_weighted_function_counts) {
+ InstrProfRecord Record1("foo", 0x1234, {1, 2});
+ InstrProfRecord Record2("foo", 0x1235, {3, 4});
+ Writer.addRecord(std::move(Record1), 3);
+ Writer.addRecord(std::move(Record2), 5);
+ auto Profile = Writer.writeBuffer();
+ readProfile(std::move(Profile));
+
+ std::vector<uint64_t> Counts;
+ ASSERT_TRUE(NoError(Reader->getFunctionCounts("foo", 0x1234, Counts)));
+ ASSERT_EQ(2U, Counts.size());
+ ASSERT_EQ(3U, Counts[0]);
+ ASSERT_EQ(6U, Counts[1]);
+
+ ASSERT_TRUE(NoError(Reader->getFunctionCounts("foo", 0x1235, Counts)));
+ ASSERT_EQ(2U, Counts.size());
+ ASSERT_EQ(15U, Counts[0]);
+ ASSERT_EQ(20U, Counts[1]);
+}
+
} // end anonymous namespace