3030#include " llvm/Support/MD5.h"
3131#include " llvm/Support/raw_ostream.h"
3232#include < algorithm>
33+ #include < cmath>
3334#include < cstdint>
3435#include < memory>
3536#include < set>
3637#include < system_error>
3738#include < utility>
3839#include < vector>
3940
41+ #define DEBUG_TYPE " llvm-profdata"
42+
4043using namespace llvm ;
4144using namespace sampleprof ;
4245
46+ namespace llvm {
47+ namespace support {
48+ namespace endian {
49+ namespace {
50+
51+ // Adapter class to llvm::support::endian::Writer for pwrite().
52+ struct SeekableWriter {
53+ raw_pwrite_stream &OS;
54+ endianness Endian;
55+ SeekableWriter (raw_pwrite_stream &OS, endianness Endian)
56+ : OS(OS), Endian(Endian) {}
57+
58+ template <typename ValueType>
59+ void pwrite (ValueType Val, size_t Offset) {
60+ std::string StringBuf;
61+ raw_string_ostream SStream (StringBuf);
62+ Writer (SStream, Endian).write (Val);
63+ OS.pwrite (StringBuf.data (), StringBuf.size (), Offset);
64+ }
65+ };
66+
67+ } // namespace
68+ } // namespace endian
69+ } // namespace support
70+ } // namespace llvm
71+
72+ DefaultFunctionPruningStrategy::DefaultFunctionPruningStrategy (
73+ SampleProfileMap &ProfileMap, size_t OutputSizeLimit)
74+ : FunctionPruningStrategy(ProfileMap, OutputSizeLimit) {
75+ sortFuncProfiles (ProfileMap, SortedFunctions);
76+ }
77+
78+ void DefaultFunctionPruningStrategy::Erase (size_t CurrentOutputSize) {
79+ double D = (double )OutputSizeLimit / CurrentOutputSize;
80+ size_t NewSize = (size_t )round (ProfileMap.size () * D * D);
81+ size_t NumToRemove = ProfileMap.size () - NewSize;
82+ if (NumToRemove < 1 )
83+ NumToRemove = 1 ;
84+
85+ assert (NumToRemove <= SortedFunctions.size ());
86+ llvm::for_each (
87+ llvm::make_range (SortedFunctions.begin () + SortedFunctions.size () -
88+ NumToRemove,
89+ SortedFunctions.end ()),
90+ [&](const NameFunctionSamples &E) { ProfileMap.erase (E.first ); });
91+ SortedFunctions.resize (SortedFunctions.size () - NumToRemove);
92+ }
93+
94+ std::error_code SampleProfileWriter::writeWithSizeLimitInternal (
95+ SampleProfileMap &ProfileMap, size_t OutputSizeLimit,
96+ FunctionPruningStrategy *Strategy) {
97+ if (OutputSizeLimit == 0 )
98+ return write (ProfileMap);
99+
100+ size_t OriginalFunctionCount = ProfileMap.size ();
101+
102+ std::unique_ptr<raw_ostream> OriginalOutputStream;
103+ OutputStream.swap (OriginalOutputStream);
104+
105+ size_t IterationCount = 0 ;
106+ size_t TotalSize;
107+
108+ SmallVector<char > StringBuffer;
109+ do {
110+ StringBuffer.clear ();
111+ OutputStream.reset (new raw_svector_ostream (StringBuffer));
112+ if (std::error_code EC = write (ProfileMap))
113+ return EC;
114+
115+ TotalSize = StringBuffer.size ();
116+ // On Windows every "\n" is actually written as "\r\n" to disk but not to
117+ // memory buffer, this difference should be added when considering the total
118+ // output size.
119+ #ifdef _WIN32
120+ if (Format == SPF_Text)
121+ TotalSize += LineCount;
122+ #endif
123+ if (TotalSize <= OutputSizeLimit)
124+ break ;
125+
126+ Strategy->Erase (TotalSize);
127+ IterationCount++;
128+ } while (ProfileMap.size () != 0 );
129+
130+ if (ProfileMap.size () == 0 )
131+ return sampleprof_error::too_large;
132+
133+ OutputStream.swap (OriginalOutputStream);
134+ OutputStream->write (StringBuffer.data (), StringBuffer.size ());
135+ LLVM_DEBUG (dbgs () << " Profile originally has " << OriginalFunctionCount
136+ << " functions, reduced to " << ProfileMap.size () << " in "
137+ << IterationCount << " iterations\n " );
138+ // Silence warning on Release build.
139+ (void )OriginalFunctionCount;
140+ (void )IterationCount;
141+ return sampleprof_error::success;
142+ }
143+
43144std::error_code
44145SampleProfileWriter::writeFuncProfiles (const SampleProfileMap &ProfileMap) {
45146 std::vector<NameFunctionSamples> V;
@@ -116,6 +217,12 @@ std::error_code SampleProfileWriterExtBinaryBase::addNewSection(
116217
117218std::error_code
118219SampleProfileWriterExtBinaryBase::write (const SampleProfileMap &ProfileMap) {
220+ // When calling write on a different profile map, existing states should be
221+ // cleared.
222+ NameTable.clear ();
223+ CSNameTable.clear ();
224+ SecHdrTable.clear ();
225+
119226 if (std::error_code EC = writeHeader (ProfileMap))
120227 return EC;
121228
@@ -477,6 +584,7 @@ std::error_code SampleProfileWriterText::writeSample(const FunctionSamples &S) {
477584 if (Indent == 0 )
478585 OS << " :" << S.getHeadSamples ();
479586 OS << " \n " ;
587+ LineCount++;
480588
481589 SampleSorter<LineLocation, SampleRecord> SortedSamples (S.getBodySamples ());
482590 for (const auto &I : SortedSamples.get ()) {
@@ -493,6 +601,7 @@ std::error_code SampleProfileWriterText::writeSample(const FunctionSamples &S) {
493601 for (const auto &J : Sample.getSortedCallTargets ())
494602 OS << " " << J.first << " :" << J.second ;
495603 OS << " \n " ;
604+ LineCount++;
496605 }
497606
498607 SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples (
@@ -515,11 +624,13 @@ std::error_code SampleProfileWriterText::writeSample(const FunctionSamples &S) {
515624 if (FunctionSamples::ProfileIsProbeBased) {
516625 OS.indent (Indent + 1 );
517626 OS << " !CFGChecksum: " << S.getFunctionHash () << " \n " ;
627+ LineCount++;
518628 }
519629
520630 if (S.getContext ().getAllAttributes ()) {
521631 OS.indent (Indent + 1 );
522632 OS << " !Attributes: " << S.getContext ().getAllAttributes () << " \n " ;
633+ LineCount++;
523634 }
524635
525636 return sampleprof_error::success;
@@ -605,14 +716,10 @@ std::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() {
605716 auto &OS = *OutputStream;
606717
607718 // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable.
608- auto &OFS = static_cast <raw_fd_ostream &>(OS);
609719 uint64_t FuncOffsetTableStart = OS.tell ();
610- if (OFS.seek (TableOffset) == (uint64_t )-1 )
611- return sampleprof_error::ostream_seek_unsupported;
612- support::endian::Writer Writer (*OutputStream, support::little);
613- Writer.write (FuncOffsetTableStart);
614- if (OFS.seek (FuncOffsetTableStart) == (uint64_t )-1 )
615- return sampleprof_error::ostream_seek_unsupported;
720+ support::endian::SeekableWriter Writer (static_cast <raw_pwrite_stream &>(OS),
721+ support::little);
722+ Writer.pwrite (FuncOffsetTableStart, TableOffset);
616723
617724 // Write out the table size.
618725 encodeULEB128 (FuncOffsetTable.size (), OS);
@@ -623,6 +730,7 @@ std::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() {
623730 return EC;
624731 encodeULEB128 (Entry.second , OS);
625732 }
733+ FuncOffsetTable.clear ();
626734 return sampleprof_error::success;
627735}
628736
@@ -650,6 +758,10 @@ SampleProfileWriterBinary::writeMagicIdent(SampleProfileFormat Format) {
650758
651759std::error_code
652760SampleProfileWriterBinary::writeHeader (const SampleProfileMap &ProfileMap) {
761+ // When calling write on a different profile map, existing names should be
762+ // cleared.
763+ NameTable.clear ();
764+
653765 writeMagicIdent (Format);
654766
655767 computeSummary (ProfileMap);
@@ -690,14 +802,6 @@ void SampleProfileWriterExtBinaryBase::allocSecHdrTable() {
690802}
691803
692804std::error_code SampleProfileWriterExtBinaryBase::writeSecHdrTable () {
693- auto &OFS = static_cast <raw_fd_ostream &>(*OutputStream);
694- uint64_t Saved = OutputStream->tell ();
695-
696- // Set OutputStream to the location saved in SecHdrTableOffset.
697- if (OFS.seek (SecHdrTableOffset) == (uint64_t )-1 )
698- return sampleprof_error::ostream_seek_unsupported;
699- support::endian::Writer Writer (*OutputStream, support::little);
700-
701805 assert (SecHdrTable.size () == SectionHdrLayout.size () &&
702806 " SecHdrTable entries doesn't match SectionHdrLayout" );
703807 SmallVector<uint32_t , 16 > IndexMap (SecHdrTable.size (), -1 );
@@ -714,21 +818,23 @@ std::error_code SampleProfileWriterExtBinaryBase::writeSecHdrTable() {
714818 // needs to be computed after SecLBRProfile (the order in SecHdrTable),
715819 // but it needs to be read before SecLBRProfile (the order in
716820 // SectionHdrLayout). So we use IndexMap above to switch the order.
821+ support::endian::SeekableWriter Writer (
822+ static_cast <raw_pwrite_stream &>(*OutputStream), support::little);
717823 for (uint32_t LayoutIdx = 0 ; LayoutIdx < SectionHdrLayout.size ();
718824 LayoutIdx++) {
719825 assert (IndexMap[LayoutIdx] < SecHdrTable.size () &&
720826 " Incorrect LayoutIdx in SecHdrTable" );
721827 auto Entry = SecHdrTable[IndexMap[LayoutIdx]];
722- Writer.write (static_cast <uint64_t >(Entry.Type ));
723- Writer.write (static_cast <uint64_t >(Entry.Flags ));
724- Writer.write (static_cast <uint64_t >(Entry.Offset ));
725- Writer.write (static_cast <uint64_t >(Entry.Size ));
828+ Writer.pwrite (static_cast <uint64_t >(Entry.Type ),
829+ SecHdrTableOffset + 4 * LayoutIdx * sizeof (uint64_t ));
830+ Writer.pwrite (static_cast <uint64_t >(Entry.Flags ),
831+ SecHdrTableOffset + (4 * LayoutIdx + 1 ) * sizeof (uint64_t ));
832+ Writer.pwrite (static_cast <uint64_t >(Entry.Offset ),
833+ SecHdrTableOffset + (4 * LayoutIdx + 2 ) * sizeof (uint64_t ));
834+ Writer.pwrite (static_cast <uint64_t >(Entry.Size ),
835+ SecHdrTableOffset + (4 * LayoutIdx + 3 ) * sizeof (uint64_t ));
726836 }
727837
728- // Reset OutputStream.
729- if (OFS.seek (Saved) == (uint64_t )-1 )
730- return sampleprof_error::ostream_seek_unsupported;
731-
732838 return sampleprof_error::success;
733839}
734840
0 commit comments