212 lines · cpp
1//===- TpiStreamBuilder.cpp - -------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"10#include "llvm/ADT/ArrayRef.h"11#include "llvm/ADT/STLExtras.h"12#include "llvm/DebugInfo/CodeView/RecordSerialization.h"13#include "llvm/DebugInfo/CodeView/TypeIndex.h"14#include "llvm/DebugInfo/MSF/MSFBuilder.h"15#include "llvm/DebugInfo/MSF/MappedBlockStream.h"16#include "llvm/DebugInfo/PDB/Native/RawTypes.h"17#include "llvm/Support/Allocator.h"18#include "llvm/Support/BinaryByteStream.h"19#include "llvm/Support/BinaryStreamWriter.h"20#include "llvm/Support/Endian.h"21#include "llvm/Support/Error.h"22#include "llvm/Support/TimeProfiler.h"23#include <cstdint>24#include <numeric>25 26using namespace llvm;27using namespace llvm::msf;28using namespace llvm::pdb;29using namespace llvm::support;30 31TpiStreamBuilder::TpiStreamBuilder(MSFBuilder &Msf, uint32_t StreamIdx)32 : Msf(Msf), Allocator(Msf.getAllocator()), Header(nullptr), Idx(StreamIdx) {33}34 35TpiStreamBuilder::~TpiStreamBuilder() = default;36 37void TpiStreamBuilder::setVersionHeader(PdbRaw_TpiVer Version) {38 VerHeader = Version;39}40 41void TpiStreamBuilder::updateTypeIndexOffsets(ArrayRef<uint16_t> Sizes) {42 // If we just crossed an 8KB threshold, add a type index offset.43 for (uint16_t Size : Sizes) {44 size_t NewSize = TypeRecordBytes + Size;45 constexpr size_t EightKB = 8 * 1024;46 if (NewSize / EightKB > TypeRecordBytes / EightKB || TypeRecordCount == 0) {47 TypeIndexOffsets.push_back(48 {codeview::TypeIndex(codeview::TypeIndex::FirstNonSimpleIndex +49 TypeRecordCount),50 ulittle32_t(TypeRecordBytes)});51 }52 ++TypeRecordCount;53 TypeRecordBytes = NewSize;54 }55}56 57void TpiStreamBuilder::addTypeRecord(ArrayRef<uint8_t> Record,58 std::optional<uint32_t> Hash) {59 assert(((Record.size() & 3) == 0) &&60 "The type record's size is not a multiple of 4 bytes which will "61 "cause misalignment in the output TPI stream!");62 assert(Record.size() <= codeview::MaxRecordLength);63 uint16_t OneSize = (uint16_t)Record.size();64 updateTypeIndexOffsets(ArrayRef(&OneSize, 1));65 66 TypeRecBuffers.push_back(Record);67 // FIXME: Require it.68 if (Hash)69 TypeHashes.push_back(*Hash);70}71 72void TpiStreamBuilder::addTypeRecords(ArrayRef<uint8_t> Types,73 ArrayRef<uint16_t> Sizes,74 ArrayRef<uint32_t> Hashes) {75 // Ignore empty type buffers. There should be no hashes or sizes in this case.76 if (Types.empty()) {77 assert(Sizes.empty() && Hashes.empty());78 return;79 }80 81 assert(((Types.size() & 3) == 0) &&82 "The type record's size is not a multiple of 4 bytes which will "83 "cause misalignment in the output TPI stream!");84 assert(Sizes.size() == Hashes.size() && "sizes and hashes should be in sync");85 assert(std::accumulate(Sizes.begin(), Sizes.end(), 0U) == Types.size() &&86 "sizes of type records should sum to the size of the types");87 updateTypeIndexOffsets(Sizes);88 89 TypeRecBuffers.push_back(Types);90 llvm::append_range(TypeHashes, Hashes);91}92 93Error TpiStreamBuilder::finalize() {94 if (Header)95 return Error::success();96 97 TpiStreamHeader *H = Allocator.Allocate<TpiStreamHeader>();98 99 H->Version = VerHeader;100 H->HeaderSize = sizeof(TpiStreamHeader);101 H->TypeIndexBegin = codeview::TypeIndex::FirstNonSimpleIndex;102 H->TypeIndexEnd = H->TypeIndexBegin + TypeRecordCount;103 H->TypeRecordBytes = TypeRecordBytes;104 105 H->HashStreamIndex = HashStreamIndex;106 H->HashAuxStreamIndex = kInvalidStreamIndex;107 H->HashKeySize = sizeof(ulittle32_t);108 H->NumHashBuckets = MaxTpiHashBuckets - 1;109 110 // Recall that hash values go into a completely different stream identified by111 // the `HashStreamIndex` field of the `TpiStreamHeader`. Therefore, the data112 // begins at offset 0 of this independent stream.113 H->HashValueBuffer.Off = 0;114 H->HashValueBuffer.Length = calculateHashBufferSize();115 116 // We never write any adjustments into our PDBs, so this is usually some117 // offset with zero length.118 H->HashAdjBuffer.Off = H->HashValueBuffer.Off + H->HashValueBuffer.Length;119 H->HashAdjBuffer.Length = 0;120 121 H->IndexOffsetBuffer.Off = H->HashAdjBuffer.Off + H->HashAdjBuffer.Length;122 H->IndexOffsetBuffer.Length = calculateIndexOffsetSize();123 124 Header = H;125 return Error::success();126}127 128uint32_t TpiStreamBuilder::calculateSerializedLength() {129 return sizeof(TpiStreamHeader) + TypeRecordBytes;130}131 132uint32_t TpiStreamBuilder::calculateHashBufferSize() const {133 assert((TypeRecordCount == TypeHashes.size() || TypeHashes.empty()) &&134 "either all or no type records should have hashes");135 return TypeHashes.size() * sizeof(ulittle32_t);136}137 138uint32_t TpiStreamBuilder::calculateIndexOffsetSize() const {139 return TypeIndexOffsets.size() * sizeof(codeview::TypeIndexOffset);140}141 142Error TpiStreamBuilder::finalizeMsfLayout() {143 uint32_t Length = calculateSerializedLength();144 if (auto EC = Msf.setStreamSize(Idx, Length))145 return EC;146 147 uint32_t HashStreamSize =148 calculateHashBufferSize() + calculateIndexOffsetSize();149 150 if (HashStreamSize == 0)151 return Error::success();152 153 auto ExpectedIndex = Msf.addStream(HashStreamSize);154 if (!ExpectedIndex)155 return ExpectedIndex.takeError();156 HashStreamIndex = *ExpectedIndex;157 if (!TypeHashes.empty()) {158 ulittle32_t *H = Allocator.Allocate<ulittle32_t>(TypeHashes.size());159 MutableArrayRef<ulittle32_t> HashBuffer(H, TypeHashes.size());160 for (uint32_t I = 0; I < TypeHashes.size(); ++I) {161 HashBuffer[I] = TypeHashes[I] % (MaxTpiHashBuckets - 1);162 }163 ArrayRef<uint8_t> Bytes(164 reinterpret_cast<const uint8_t *>(HashBuffer.data()),165 calculateHashBufferSize());166 HashValueStream =167 std::make_unique<BinaryByteStream>(Bytes, llvm::endianness::little);168 }169 return Error::success();170}171 172Error TpiStreamBuilder::commit(const msf::MSFLayout &Layout,173 WritableBinaryStreamRef Buffer) {174 llvm::TimeTraceScope timeScope("Commit TPI stream");175 if (auto EC = finalize())176 return EC;177 178 auto InfoS = WritableMappedBlockStream::createIndexedStream(Layout, Buffer,179 Idx, Allocator);180 181 BinaryStreamWriter Writer(*InfoS);182 if (auto EC = Writer.writeObject(*Header))183 return EC;184 185 for (auto Rec : TypeRecBuffers) {186 assert(!Rec.empty() && "Attempting to write an empty type record shifts "187 "all offsets in the TPI stream!");188 assert(((Rec.size() & 3) == 0) &&189 "The type record's size is not a multiple of 4 bytes which will "190 "cause misalignment in the output TPI stream!");191 if (auto EC = Writer.writeBytes(Rec))192 return EC;193 }194 195 if (HashStreamIndex != kInvalidStreamIndex) {196 auto HVS = WritableMappedBlockStream::createIndexedStream(197 Layout, Buffer, HashStreamIndex, Allocator);198 BinaryStreamWriter HW(*HVS);199 if (HashValueStream) {200 if (auto EC = HW.writeStreamRef(*HashValueStream))201 return EC;202 }203 204 for (auto &IndexOffset : TypeIndexOffsets) {205 if (auto EC = HW.writeObject(IndexOffset))206 return EC;207 }208 }209 210 return Error::success();211}212