Previously MappedBlockStream owned its own BumpPtrAllocator that it would allocate from when a read crossed a block boundary. This way it could still return the user a contiguous buffer of the requested size. However, It's not uncommon to open a stream, read some stuff, close it, and then save the information for later. After all, since the entire file is mapped into memory, the data should always be available as long as the file is open. Of course, the exception to this is when the data isn't *in* the file, but rather in some buffer that we temporarily allocated to present this contiguous view. And this buffer would get destroyed as soon as the strema was closed. The fix here is to force the user to specify the allocator, this way it can provide an allocator that has whatever lifetime it chooses. Differential Revision: https://reviews.llvm.org/D33858 llvm-svn: 304623
178 lines
5.9 KiB
C++
178 lines
5.9 KiB
C++
//===- TpiStreamBuilder.cpp - -------------------------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
|
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
|
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
|
|
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
|
|
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
|
#include "llvm/DebugInfo/PDB/Native/RawError.h"
|
|
#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
|
|
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
|
|
#include "llvm/Support/Allocator.h"
|
|
#include "llvm/Support/BinaryByteStream.h"
|
|
#include "llvm/Support/BinaryStreamArray.h"
|
|
#include "llvm/Support/BinaryStreamReader.h"
|
|
#include "llvm/Support/BinaryStreamWriter.h"
|
|
#include "llvm/Support/Endian.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::msf;
|
|
using namespace llvm::pdb;
|
|
using namespace llvm::support;
|
|
|
|
TpiStreamBuilder::TpiStreamBuilder(MSFBuilder &Msf, uint32_t StreamIdx)
|
|
: Msf(Msf), Allocator(Msf.getAllocator()), Header(nullptr), Idx(StreamIdx) {
|
|
}
|
|
|
|
TpiStreamBuilder::~TpiStreamBuilder() = default;
|
|
|
|
void TpiStreamBuilder::setVersionHeader(PdbRaw_TpiVer Version) {
|
|
VerHeader = Version;
|
|
}
|
|
|
|
void TpiStreamBuilder::addTypeRecord(ArrayRef<uint8_t> Record,
|
|
Optional<uint32_t> Hash) {
|
|
// If we just crossed an 8KB threshold, add a type index offset.
|
|
size_t NewSize = TypeRecordBytes + Record.size();
|
|
constexpr size_t EightKB = 8 * 1024;
|
|
if (NewSize / EightKB > TypeRecordBytes / EightKB || TypeRecords.empty()) {
|
|
TypeIndexOffsets.push_back(
|
|
{codeview::TypeIndex(codeview::TypeIndex::FirstNonSimpleIndex +
|
|
TypeRecords.size()),
|
|
ulittle32_t(TypeRecordBytes)});
|
|
}
|
|
TypeRecordBytes = NewSize;
|
|
|
|
TypeRecords.push_back(Record);
|
|
if (Hash)
|
|
TypeHashes.push_back(*Hash);
|
|
}
|
|
|
|
Error TpiStreamBuilder::finalize() {
|
|
if (Header)
|
|
return Error::success();
|
|
|
|
TpiStreamHeader *H = Allocator.Allocate<TpiStreamHeader>();
|
|
|
|
uint32_t Count = TypeRecords.size();
|
|
|
|
H->Version = VerHeader;
|
|
H->HeaderSize = sizeof(TpiStreamHeader);
|
|
H->TypeIndexBegin = codeview::TypeIndex::FirstNonSimpleIndex;
|
|
H->TypeIndexEnd = H->TypeIndexBegin + Count;
|
|
H->TypeRecordBytes = TypeRecordBytes;
|
|
|
|
H->HashStreamIndex = HashStreamIndex;
|
|
H->HashAuxStreamIndex = kInvalidStreamIndex;
|
|
H->HashKeySize = sizeof(ulittle32_t);
|
|
H->NumHashBuckets = MinTpiHashBuckets;
|
|
|
|
// Recall that hash values go into a completely different stream identified by
|
|
// the `HashStreamIndex` field of the `TpiStreamHeader`. Therefore, the data
|
|
// begins at offset 0 of this independent stream.
|
|
H->HashValueBuffer.Off = 0;
|
|
H->HashValueBuffer.Length = calculateHashBufferSize();
|
|
|
|
// We never write any adjustments into our PDBs, so this is usually some
|
|
// offset with zero length.
|
|
H->HashAdjBuffer.Off = H->HashValueBuffer.Off + H->HashValueBuffer.Length;
|
|
H->HashAdjBuffer.Length = 0;
|
|
|
|
H->IndexOffsetBuffer.Off = H->HashAdjBuffer.Off + H->HashAdjBuffer.Length;
|
|
H->IndexOffsetBuffer.Length = calculateIndexOffsetSize();
|
|
|
|
Header = H;
|
|
return Error::success();
|
|
}
|
|
|
|
uint32_t TpiStreamBuilder::calculateSerializedLength() {
|
|
return sizeof(TpiStreamHeader) + TypeRecordBytes;
|
|
}
|
|
|
|
uint32_t TpiStreamBuilder::calculateHashBufferSize() const {
|
|
assert((TypeRecords.size() == TypeHashes.size() || TypeHashes.empty()) &&
|
|
"either all or no type records should have hashes");
|
|
return TypeHashes.size() * sizeof(ulittle32_t);
|
|
}
|
|
|
|
uint32_t TpiStreamBuilder::calculateIndexOffsetSize() const {
|
|
return TypeIndexOffsets.size() * sizeof(codeview::TypeIndexOffset);
|
|
}
|
|
|
|
Error TpiStreamBuilder::finalizeMsfLayout() {
|
|
uint32_t Length = calculateSerializedLength();
|
|
if (auto EC = Msf.setStreamSize(Idx, Length))
|
|
return EC;
|
|
|
|
uint32_t HashStreamSize =
|
|
calculateHashBufferSize() + calculateIndexOffsetSize();
|
|
|
|
if (HashStreamSize == 0)
|
|
return Error::success();
|
|
|
|
auto ExpectedIndex = Msf.addStream(HashStreamSize);
|
|
if (!ExpectedIndex)
|
|
return ExpectedIndex.takeError();
|
|
HashStreamIndex = *ExpectedIndex;
|
|
if (!TypeHashes.empty()) {
|
|
ulittle32_t *H = Allocator.Allocate<ulittle32_t>(TypeHashes.size());
|
|
MutableArrayRef<ulittle32_t> HashBuffer(H, TypeHashes.size());
|
|
for (uint32_t I = 0; I < TypeHashes.size(); ++I) {
|
|
HashBuffer[I] = TypeHashes[I] % MinTpiHashBuckets;
|
|
}
|
|
ArrayRef<uint8_t> Bytes(
|
|
reinterpret_cast<const uint8_t *>(HashBuffer.data()),
|
|
calculateHashBufferSize());
|
|
HashValueStream =
|
|
llvm::make_unique<BinaryByteStream>(Bytes, llvm::support::little);
|
|
}
|
|
return Error::success();
|
|
}
|
|
|
|
Error TpiStreamBuilder::commit(const msf::MSFLayout &Layout,
|
|
WritableBinaryStreamRef Buffer) {
|
|
if (auto EC = finalize())
|
|
return EC;
|
|
|
|
auto InfoS = WritableMappedBlockStream::createIndexedStream(Layout, Buffer,
|
|
Idx, Allocator);
|
|
|
|
BinaryStreamWriter Writer(*InfoS);
|
|
if (auto EC = Writer.writeObject(*Header))
|
|
return EC;
|
|
|
|
for (auto Rec : TypeRecords)
|
|
if (auto EC = Writer.writeBytes(Rec))
|
|
return EC;
|
|
|
|
if (HashStreamIndex != kInvalidStreamIndex) {
|
|
auto HVS = WritableMappedBlockStream::createIndexedStream(
|
|
Layout, Buffer, HashStreamIndex, Allocator);
|
|
BinaryStreamWriter HW(*HVS);
|
|
if (HashValueStream) {
|
|
if (auto EC = HW.writeStreamRef(*HashValueStream))
|
|
return EC;
|
|
}
|
|
|
|
for (auto &IndexOffset : TypeIndexOffsets) {
|
|
if (auto EC = HW.writeObject(IndexOffset))
|
|
return EC;
|
|
}
|
|
}
|
|
|
|
return Error::success();
|
|
}
|