brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.4 KiB · a33f147 Raw
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