1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//
5// File: HotHeapWriter.cpp
6//
7
8//
9// Class code:HotHeapWriter represents a writer of hot heap into MetaData hot stream (collected by IBC).
10//
11// ======================================================================================
12
13#include "external.h"
14
15#include "hotheapwriter.h"
16#include "../heaps/export.h"
17
18#include <stgpool.h>
19#include <metamodelpub.h>
20#include <utilcode.h>
21#include "../inc/streamutil.h"
22
23#include "hotdataformat.h"
24
25#ifdef FEATURE_PREJIT
26// Cannot be included without FEATURE_PREJIT:
27#include <corcompile.h>
28#endif //FEATURE_PREJIT
29
30namespace MetaData
31{
32
33// --------------------------------------------------------------------------------------
34//
35// Creates writer for #String heap.
36//
37HotHeapWriter::HotHeapWriter(
38 const StringHeapRW *pStringHeap)
39{
40 m_HeapIndex = HeapIndex::StringHeapIndex;
41 m_pStringHeap = pStringHeap;
42} // HotHeapWriter::HotHeapWriter
43
44// --------------------------------------------------------------------------------------
45//
46// Creates writer for #Blob or #US heap (if fUserStringHeap is TRUE).
47//
48HotHeapWriter::HotHeapWriter(
49 const BlobHeapRW *pBlobHeap,
50 BOOL fUserStringHeap)
51{
52 m_HeapIndex = fUserStringHeap ? HeapIndex::UserStringHeapIndex : HeapIndex::BlobHeapIndex;
53 m_pBlobHeap = pBlobHeap;
54} // HotHeapWriter::HotHeapWriter
55
56// --------------------------------------------------------------------------------------
57//
58// Creates writer for #GUID heap.
59//
60HotHeapWriter::HotHeapWriter(
61 const GuidHeapRW *pGuidHeap)
62{
63 m_HeapIndex = HeapIndex::GuidHeapIndex;
64 m_pGuidHeap = pGuidHeap;
65} // HotHeapWriter::HotHeapWriter
66
67// --------------------------------------------------------------------------------------
68//
69// Destroys the writer of hot heap.
70//
71void
72HotHeapWriter::Delete()
73{
74} // HotHeapWriter::Delete
75
76typedef struct _RidOffsetPair
77{
78 ULONG rid;
79 ULONG offset;
80 // compare function for qsort
81 static int __cdecl Compare(void const *_x, void const *_y);
82} RidOffsetPair;
83
84// static
85int __cdecl
86RidOffsetPair::Compare(void const *_x, void const *_y)
87{
88 RidOffsetPair const *x = reinterpret_cast<RidOffsetPair const *>(_x);
89 RidOffsetPair const *y = reinterpret_cast<RidOffsetPair const *>(_y);
90
91 return x->rid - y->rid;
92}
93
94// --------------------------------------------------------------------------------------
95//
96// Stores hot data reported by IBC in profile data (code:CorProfileData) to a stream.
97// Aligns output stream to 4-bytes.
98//
99__checkReturn
100HRESULT
101HotHeapWriter::SaveToStream(
102 IStream *pStream,
103 CorProfileData *pProfileData,
104 UINT32 *pnSavedSize) const
105{
106 _ASSERTE(pStream != NULL);
107 _ASSERTE(pProfileData != NULL);
108 _ASSERTE(pnSavedSize != NULL);
109
110#ifdef FEATURE_PREJIT
111 HRESULT hr = S_OK;
112 UINT32 nOffset = 0;
113 UINT32 nValueHeapStart_PositiveOffset;
114 UINT32 nValueOffsetTableStart_PositiveOffset;
115 UINT32 nIndexTableStart_PositiveOffset;
116
117 // data
118 //
119
120 // number of hot tokens
121 UINT32 nHotItemsCount = pProfileData->GetHotTokens(
122 GetTableIndex(),
123 1 << ProfilingFlags_MetaData,
124 1 << ProfilingFlags_MetaData,
125 NULL,
126 0);
127 CONSISTENCY_CHECK(nHotItemsCount != 0);
128
129 NewArrayHolder<UINT32> hotItemArr = new (nothrow) UINT32[nHotItemsCount];
130 IfNullRet(hotItemArr);
131
132 // get hot tokens
133 static_assert_no_msg(sizeof(UINT32) == sizeof(mdToken));
134 pProfileData->GetHotTokens(
135 GetTableIndex(),
136 1 << ProfilingFlags_MetaData,
137 1 << ProfilingFlags_MetaData,
138 reinterpret_cast<mdToken *>(&hotItemArr[0]),
139 nHotItemsCount);
140
141 // convert tokens to rids
142 for (UINT32 i = 0; i < nHotItemsCount; i++)
143 {
144 hotItemArr[i] = RidFromToken(hotItemArr[i]);
145 }
146
147 NewArrayHolder<RidOffsetPair> offsetMapping = new (nothrow) RidOffsetPair[nHotItemsCount];
148 IfNullRet(offsetMapping);
149
150 // write data
151 nValueHeapStart_PositiveOffset = nOffset;
152
153 // note that we write hot items in the order they appear in pProfileData->GetHotTokens
154 // this is so that we preserve the ordering optimizations done by IbcMerge
155 for (UINT32 i = 0; i < nHotItemsCount; i++)
156 {
157 DataBlob data;
158 IfFailRet(GetData(
159 hotItemArr[i],
160 &data));
161
162 // keep track of the offset at which each hot item is written
163 offsetMapping[i].rid = hotItemArr[i];
164 offsetMapping[i].offset = nOffset;
165
166 IfFailRet(StreamUtil::WriteToStream(
167 pStream,
168 data.GetDataPointer(),
169 data.GetSize(),
170 &nOffset));
171 }
172
173 IfFailRet(StreamUtil::AlignDWORD(pStream, &nOffset));
174
175 // sort by rid so that a hot rid can be looked up by binary search
176 qsort(offsetMapping, nHotItemsCount, sizeof(RidOffsetPair), RidOffsetPair::Compare);
177
178 // initialize table of offsets to data
179 NewArrayHolder<UINT32> dataIndices = new (nothrow) UINT32[nHotItemsCount];
180 IfNullRet(dataIndices);
181
182 // fill in the hotItemArr (now sorted by rid) and dataIndices array with each offset
183 for (UINT32 i = 0; i < nHotItemsCount; i++)
184 {
185 hotItemArr[i] = offsetMapping[i].rid;
186 dataIndices[i] = offsetMapping[i].offset;
187 }
188
189 // table of offsets to data
190 //
191
192 nValueOffsetTableStart_PositiveOffset = nOffset;
193 IfFailRet(StreamUtil::WriteToStream(pStream, &dataIndices[0], sizeof(UINT32) * nHotItemsCount, &nOffset));
194
195 // rid table (sorted)
196 //
197
198 nIndexTableStart_PositiveOffset = nOffset;
199
200 IfFailRet(StreamUtil::WriteToStream(pStream, &hotItemArr[0], nHotItemsCount * sizeof(UINT32), &nOffset));
201 IfFailRet(StreamUtil::AlignDWORD(pStream, &nOffset));
202
203 {
204 // hot pool header
205 struct HotHeapHeader header;
206
207 // fix offsets
208 header.m_nIndexTableStart_NegativeOffset = nOffset - nIndexTableStart_PositiveOffset;
209 header.m_nValueOffsetTableStart_NegativeOffset = nOffset - nValueOffsetTableStart_PositiveOffset;
210 header.m_nValueHeapStart_NegativeOffset = nOffset - nValueHeapStart_PositiveOffset;
211
212 // write header
213 IfFailRet(StreamUtil::WriteToStream(pStream, &header, sizeof(header), &nOffset));
214 }
215
216 *pnSavedSize = nOffset;
217
218#endif //FEATURE_PREJIT
219
220 return S_OK;
221} // HotHeapWriter::PersistHotToStream
222
223// --------------------------------------------------------------------------------------
224//
225// Returns index of the heap as table index used by IBC (code:CorProfileData).
226//
227UINT32
228HotHeapWriter::GetTableIndex() const
229{
230 return TBL_COUNT + m_HeapIndex.Get();
231} // HotHeapWriter::GetTableIndex
232
233// --------------------------------------------------------------------------------------
234//
235// Returns heap data at index (nIndex).
236//
237__checkReturn
238HRESULT
239HotHeapWriter::GetData(
240 UINT32 nIndex,
241 DataBlob *pData) const
242{
243 HRESULT hr;
244
245 switch (m_HeapIndex.Get())
246 {
247 case HeapIndex::StringHeapIndex:
248 {
249 LPCSTR szString;
250 IfFailGo(m_pStringHeap->GetString(
251 nIndex,
252 &szString));
253 _ASSERTE(hr == S_OK);
254
255 // This should not overflow, because we checked it before, but it doesn't hurt
256 S_UINT32 cbStringSize = S_UINT32(strlen(szString)) + S_UINT32(1);
257 if (cbStringSize.IsOverflow())
258 {
259 Debug_ReportInternalError("There's a bug in the string heap consistency - string is too long.");
260 IfFailGo(METADATA_E_INTERNAL_ERROR);
261 }
262
263 pData->Init((BYTE *)szString, cbStringSize.Value());
264 return S_OK;
265 }
266 case HeapIndex::GuidHeapIndex:
267 {
268 // The nIndex is in fact 0-based offset into GUID heap (0, 16, 32, ...), convert it to 1-based element index (1, 2, 3, ...) for GetGuid method
269 if ((nIndex % sizeof(GUID)) != 0)
270 {
271 Debug_ReportInternalError("There's a bug in the caller/IBC - this should be GUID offset aligned to 16-B.");
272 IfFailGo(METADATA_E_INTERNAL_ERROR);
273 }
274 nIndex = (nIndex / sizeof(GUID)) + 1;
275
276 GUID UNALIGNED *pGuid;
277 IfFailGo(const_cast<GuidHeapRW *>(m_pGuidHeap)->GetGuid(
278 nIndex,
279 &pGuid));
280 _ASSERTE(hr == S_OK);
281 pData->Init((BYTE *)pGuid, sizeof(GUID));
282 return S_OK;
283 }
284 case HeapIndex::BlobHeapIndex:
285 case HeapIndex::UserStringHeapIndex:
286 {
287 IfFailGo(const_cast<BlobHeapRW *>(m_pBlobHeap)->GetBlobWithSizePrefix(
288 nIndex,
289 pData));
290 _ASSERTE(hr == S_OK);
291
292 return S_OK;
293 }
294 default:
295 Debug_ReportInternalError("There's a bug in the caller - this is wrong heap index.");
296 IfFailGo(METADATA_E_INTERNAL_ERROR);
297 }
298 return S_OK;
299
300ErrExit:
301 pData->Clear();
302 return hr;
303} // HotHeapWriter::GetData
304
305}; // namespace MetaData
306