Skip to content
Merged
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
12 changes: 12 additions & 0 deletions bolt/include/bolt/Core/DebugData.h
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,12 @@ class DebugStrOffsetsWriter {
return std::move(StrOffsetsBuffer);
}

/// Returns strings of .debug_str_offsets.
StringRef getBufferStr() {
return StringRef(reinterpret_cast<const char *>(StrOffsetsBuffer->data()),
StrOffsetsBuffer->size());
}

/// Initializes Buffer and Stream.
void initialize(DWARFUnit &Unit);

Expand Down Expand Up @@ -507,6 +513,12 @@ class DebugStrWriter {
return std::move(StrBuffer);
}

/// Returns strings of .debug_str.
StringRef getBufferStr() {
return StringRef(reinterpret_cast<const char *>(StrBuffer->data()),
StrBuffer->size());
}

/// Adds string to .debug_str.
/// On first invocation it initializes internal data structures.
uint32_t addString(StringRef Str);
Expand Down
120 changes: 118 additions & 2 deletions bolt/lib/Rewrite/DWARFRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1723,7 +1723,76 @@ StringRef getSectionName(const SectionRef &Section) {
return Name;
}

// Extracts an appropriate slice if input is DWP.
/// Extracts the slice of the .debug_str.dwo section for a given CU from a DWP
/// file, based on the .debug_str_offsets.dwo section. This helps address DWO
/// bloat that may occur after updates.
///
/// A slice of .debug_str.dwo may be composed of several non-contiguous
/// fragments. These non-contiguous string views will be written out
/// sequentially, avoiding the copying overhead caused by assembling them.
///
/// The .debug_str_offsets for the first CU often does not need to be updated,
/// so copying is only performed when .debug_str_offsets requires updating.
static void UpdateStrAndStrOffsets(StringRef StrDWOContent,
StringRef StrOffsetsContent,
SmallVectorImpl<StringRef> &StrDWOOutData,
std::string &StrOffsetsOutData,
unsigned DwarfVersion, bool IsLittleEndian) {
const llvm::endianness Endian =
IsLittleEndian ? llvm::endianness::little : llvm::endianness::big;
const uint64_t HeaderOffset = (DwarfVersion >= 5) ? 8 : 0;
constexpr size_t SizeOfOffset = sizeof(int32_t);
const uint64_t NumOffsets =
(StrOffsetsContent.size() - HeaderOffset) / SizeOfOffset;

DataExtractor Extractor(StrOffsetsContent, IsLittleEndian, 0);
uint64_t ExtractionOffset = HeaderOffset;

using StringFragment = DWARFUnitIndex::Entry::SectionContribution;
const auto getStringLength = [](StringRef Content,
uint64_t Offset) -> uint64_t {
size_t NullPos = Content.find('\0', Offset);
return (NullPos != StringRef::npos) ? (NullPos - Offset + 1) : 0;
};
const auto isContiguous = [](const StringFragment &Fragment,
uint64_t NextOffset) -> bool {
return NextOffset == Fragment.getOffset() + Fragment.getLength();
};
std::optional<StringFragment> CurrentFragment;
uint64_t AccumulatedStrLen = 0;
for (uint64_t I = 0; I < NumOffsets; ++I) {
const uint64_t StrOffset = Extractor.getU32(&ExtractionOffset);
const uint64_t StringLength = getStringLength(StrDWOContent, StrOffset);
if (!CurrentFragment) {
// First init.
CurrentFragment = StringFragment(StrOffset, StringLength);
} else {
if (isContiguous(*CurrentFragment, StrOffset)) {
// Expanding the current fragment.
CurrentFragment->setLength(CurrentFragment->getLength() + StringLength);
} else {
// Saving the current fragment and start a new one.
StrDWOOutData.push_back(StrDWOContent.substr(
CurrentFragment->getOffset(), CurrentFragment->getLength()));
CurrentFragment = StringFragment(StrOffset, StringLength);
}
}
if (AccumulatedStrLen != StrOffset) {
// Updating str offsets.
if (StrOffsetsOutData.empty())
StrOffsetsOutData = StrOffsetsContent.str();
llvm::support::endian::write32(
&StrOffsetsOutData[HeaderOffset + I * SizeOfOffset],
static_cast<uint32_t>(AccumulatedStrLen), Endian);
}
AccumulatedStrLen += StringLength;
}
if (CurrentFragment)
StrDWOOutData.push_back(StrDWOContent.substr(CurrentFragment->getOffset(),
CurrentFragment->getLength()));
}

// Exctracts an appropriate slice if input is DWP.
// Applies patches or overwrites the section.
std::optional<StringRef> updateDebugData(
DWARFContext &DWCtx, StringRef SectionName, StringRef SectionContents,
Expand Down Expand Up @@ -1772,6 +1841,8 @@ std::optional<StringRef> updateDebugData(
errs() << "BOLT-WARNING: unsupported debug section: " << SectionName
<< "\n";
if (StrWriter.isInitialized()) {
if (CUDWOEntry)
return StrWriter.getBufferStr();
OutputBuffer = StrWriter.releaseBuffer();
return StringRef(reinterpret_cast<const char *>(OutputBuffer->data()),
OutputBuffer->size());
Expand All @@ -1786,6 +1857,8 @@ std::optional<StringRef> updateDebugData(
}
case DWARFSectionKind::DW_SECT_STR_OFFSETS: {
if (StrOffstsWriter.isFinalized()) {
if (CUDWOEntry)
return StrOffstsWriter.getBufferStr();
OutputBuffer = StrOffstsWriter.releaseBuffer();
return StringRef(reinterpret_cast<const char *>(OutputBuffer->data()),
OutputBuffer->size());
Expand Down Expand Up @@ -1888,18 +1961,61 @@ void DWARFRewriter::writeDWOFiles(
}
}

StringRef StrDWOContent;
StringRef StrOffsetsContent;
llvm::SmallVector<StringRef, 3> StrDWOOutData;
std::string StrOffsetsOutData;
for (const SectionRef &Section : File->sections()) {
std::unique_ptr<DebugBufferVector> OutputData;
StringRef SectionName = getSectionName(Section);
if (SectionName == "debug_rnglists.dwo")
continue;
Expected<StringRef> ContentsExp = Section.getContents();
assert(ContentsExp && "Invalid contents.");
if (IsDWP && SectionName == "debug_str.dwo") {
if (StrWriter.isInitialized())
StrDWOContent = StrWriter.getBufferStr();
else
StrDWOContent = *ContentsExp;
continue;
}
if (std::optional<StringRef> OutData = updateDebugData(
(*DWOCU)->getContext(), SectionName, *ContentsExp, KnownSections,
*Streamer, *this, CUDWOEntry, DWOId, OutputData, RangeListssWriter,
LocWriter, StrOffstsWriter, StrWriter, OverridenSections))
LocWriter, StrOffstsWriter, StrWriter, OverridenSections)) {
if (IsDWP && SectionName == "debug_str_offsets.dwo") {
StrOffsetsContent = *OutData;
continue;
}
Streamer->emitBytes(*OutData);
}
}

if (IsDWP) {
// Handling both .debug_str.dwo and .debug_str_offsets.dwo concurrently. In
// the original DWP, .debug_str is a deduplicated global table, and the
// .debug_str.dwo slice for a single CU needs to be extracted according to
// .debug_str_offsets.dwo.
UpdateStrAndStrOffsets(StrDWOContent, StrOffsetsContent, StrDWOOutData,
StrOffsetsOutData, CU.getVersion(),
(*DWOCU)->getContext().isLittleEndian());
auto SectionIter = KnownSections.find("debug_str.dwo");
if (SectionIter != KnownSections.end()) {
Streamer->switchSection(SectionIter->second.first);
for (size_t i = 0; i < StrDWOOutData.size(); ++i) {
StringRef OutData = StrDWOOutData[i];
if (!OutData.empty())
Streamer->emitBytes(OutData);
}
}
SectionIter = KnownSections.find("debug_str_offsets.dwo");
if (SectionIter != KnownSections.end()) {
Streamer->switchSection(SectionIter->second.first);
if (!StrOffsetsOutData.empty())
Streamer->emitBytes(StrOffsetsOutData);
else
Streamer->emitBytes(StrOffsetsContent);
}
}
Streamer->finish();
TempOut->keep();
Expand Down
Loading
Loading