Line data Source code
1 : //===- TpiStreamBuilder.cpp - -------------------------------------------===//
2 : //
3 : // The LLVM Compiler Infrastructure
4 : //
5 : // This file is distributed under the University of Illinois Open Source
6 : // License. See LICENSE.TXT for details.
7 : //
8 : //===----------------------------------------------------------------------===//
9 :
10 : #include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
11 : #include "llvm/ADT/ArrayRef.h"
12 : #include "llvm/ADT/STLExtras.h"
13 : #include "llvm/DebugInfo/CodeView/TypeIndex.h"
14 : #include "llvm/DebugInfo/CodeView/TypeRecord.h"
15 : #include "llvm/DebugInfo/MSF/MSFBuilder.h"
16 : #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
17 : #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
18 : #include "llvm/DebugInfo/PDB/Native/RawError.h"
19 : #include "llvm/DebugInfo/PDB/Native/RawTypes.h"
20 : #include "llvm/Support/Allocator.h"
21 : #include "llvm/Support/BinaryByteStream.h"
22 : #include "llvm/Support/BinaryStreamArray.h"
23 : #include "llvm/Support/BinaryStreamReader.h"
24 : #include "llvm/Support/BinaryStreamWriter.h"
25 : #include "llvm/Support/Endian.h"
26 : #include "llvm/Support/Error.h"
27 : #include <algorithm>
28 : #include <cstdint>
29 :
30 : using namespace llvm;
31 : using namespace llvm::msf;
32 : using namespace llvm::pdb;
33 : using namespace llvm::support;
34 :
35 222 : TpiStreamBuilder::TpiStreamBuilder(MSFBuilder &Msf, uint32_t StreamIdx)
36 666 : : Msf(Msf), Allocator(Msf.getAllocator()), Header(nullptr), Idx(StreamIdx) {
37 222 : }
38 :
39 : TpiStreamBuilder::~TpiStreamBuilder() = default;
40 :
41 216 : void TpiStreamBuilder::setVersionHeader(PdbRaw_TpiVer Version) {
42 216 : VerHeader = Version;
43 216 : }
44 :
45 1493 : void TpiStreamBuilder::addTypeRecord(ArrayRef<uint8_t> Record,
46 : Optional<uint32_t> Hash) {
47 : // If we just crossed an 8KB threshold, add a type index offset.
48 1493 : size_t NewSize = TypeRecordBytes + Record.size();
49 : constexpr size_t EightKB = 8 * 1024;
50 1493 : if (NewSize / EightKB > TypeRecordBytes / EightKB || TypeRecords.empty()) {
51 174 : TypeIndexOffsets.push_back(
52 : {codeview::TypeIndex(codeview::TypeIndex::FirstNonSimpleIndex +
53 87 : TypeRecords.size()),
54 : ulittle32_t(TypeRecordBytes)});
55 : }
56 1493 : TypeRecordBytes = NewSize;
57 :
58 1493 : TypeRecords.push_back(Record);
59 1493 : if (Hash)
60 1994 : TypeHashes.push_back(*Hash);
61 1493 : }
62 :
63 222 : Error TpiStreamBuilder::finalize() {
64 222 : if (Header)
65 : return Error::success();
66 :
67 222 : TpiStreamHeader *H = Allocator.Allocate<TpiStreamHeader>();
68 :
69 222 : uint32_t Count = TypeRecords.size();
70 :
71 222 : H->Version = VerHeader;
72 : H->HeaderSize = sizeof(TpiStreamHeader);
73 : H->TypeIndexBegin = codeview::TypeIndex::FirstNonSimpleIndex;
74 222 : H->TypeIndexEnd = H->TypeIndexBegin + Count;
75 222 : H->TypeRecordBytes = TypeRecordBytes;
76 :
77 222 : H->HashStreamIndex = HashStreamIndex;
78 : H->HashAuxStreamIndex = kInvalidStreamIndex;
79 : H->HashKeySize = sizeof(ulittle32_t);
80 : H->NumHashBuckets = MinTpiHashBuckets;
81 :
82 : // Recall that hash values go into a completely different stream identified by
83 : // the `HashStreamIndex` field of the `TpiStreamHeader`. Therefore, the data
84 : // begins at offset 0 of this independent stream.
85 : H->HashValueBuffer.Off = 0;
86 222 : H->HashValueBuffer.Length = calculateHashBufferSize();
87 :
88 : // We never write any adjustments into our PDBs, so this is usually some
89 : // offset with zero length.
90 222 : H->HashAdjBuffer.Off = H->HashValueBuffer.Off + H->HashValueBuffer.Length;
91 : H->HashAdjBuffer.Length = 0;
92 :
93 : H->IndexOffsetBuffer.Off = H->HashAdjBuffer.Off + H->HashAdjBuffer.Length;
94 222 : H->IndexOffsetBuffer.Length = calculateIndexOffsetSize();
95 :
96 222 : Header = H;
97 : return Error::success();
98 : }
99 :
100 222 : uint32_t TpiStreamBuilder::calculateSerializedLength() {
101 222 : return sizeof(TpiStreamHeader) + TypeRecordBytes;
102 : }
103 :
104 508 : uint32_t TpiStreamBuilder::calculateHashBufferSize() const {
105 : assert((TypeRecords.size() == TypeHashes.size() || TypeHashes.empty()) &&
106 : "either all or no type records should have hashes");
107 1016 : return TypeHashes.size() * sizeof(ulittle32_t);
108 : }
109 :
110 444 : uint32_t TpiStreamBuilder::calculateIndexOffsetSize() const {
111 888 : return TypeIndexOffsets.size() * sizeof(codeview::TypeIndexOffset);
112 : }
113 :
114 222 : Error TpiStreamBuilder::finalizeMsfLayout() {
115 222 : uint32_t Length = calculateSerializedLength();
116 444 : if (auto EC = Msf.setStreamSize(Idx, Length))
117 : return EC;
118 :
119 : uint32_t HashStreamSize =
120 222 : calculateHashBufferSize() + calculateIndexOffsetSize();
121 :
122 222 : if (HashStreamSize == 0)
123 : return Error::success();
124 :
125 86 : auto ExpectedIndex = Msf.addStream(HashStreamSize);
126 86 : if (!ExpectedIndex)
127 : return ExpectedIndex.takeError();
128 86 : HashStreamIndex = *ExpectedIndex;
129 86 : if (!TypeHashes.empty()) {
130 64 : ulittle32_t *H = Allocator.Allocate<ulittle32_t>(TypeHashes.size());
131 64 : MutableArrayRef<ulittle32_t> HashBuffer(H, TypeHashes.size());
132 1061 : for (uint32_t I = 0; I < TypeHashes.size(); ++I) {
133 997 : HashBuffer[I] = TypeHashes[I] % MinTpiHashBuckets;
134 : }
135 : ArrayRef<uint8_t> Bytes(
136 : reinterpret_cast<const uint8_t *>(HashBuffer.data()),
137 64 : calculateHashBufferSize());
138 : HashValueStream =
139 : llvm::make_unique<BinaryByteStream>(Bytes, llvm::support::little);
140 : }
141 : return Error::success();
142 : }
143 :
144 222 : Error TpiStreamBuilder::commit(const msf::MSFLayout &Layout,
145 : WritableBinaryStreamRef Buffer) {
146 444 : if (auto EC = finalize())
147 : return EC;
148 :
149 : auto InfoS = WritableMappedBlockStream::createIndexedStream(Layout, Buffer,
150 888 : Idx, Allocator);
151 :
152 222 : BinaryStreamWriter Writer(*InfoS);
153 444 : if (auto EC = Writer.writeObject(*Header))
154 : return EC;
155 :
156 1715 : for (auto Rec : TypeRecords)
157 2986 : if (auto EC = Writer.writeBytes(Rec))
158 : return EC;
159 :
160 222 : if (HashStreamIndex != kInvalidStreamIndex) {
161 : auto HVS = WritableMappedBlockStream::createIndexedStream(
162 258 : Layout, Buffer, HashStreamIndex, Allocator);
163 86 : BinaryStreamWriter HW(*HVS);
164 86 : if (HashValueStream) {
165 128 : if (auto EC = HW.writeStreamRef(*HashValueStream))
166 : return EC;
167 : }
168 :
169 173 : for (auto &IndexOffset : TypeIndexOffsets) {
170 87 : if (auto EC = HW.writeObject(IndexOffset))
171 : return EC;
172 : }
173 : }
174 :
175 : return Error::success();
176 : }
|