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#ifndef _METADATATRACKER_H_
6#define _METADATATRACKER_H_
7
8#if defined(FEATURE_PREJIT) && defined(FEATURE_WINDOWSPHONE)
9
10#define METADATATRACKER_DATA 1
11#if !defined(DACCESS_COMPILE)
12#define METADATATRACKER_ENABLED 1
13#endif
14
15#endif
16
17#if METADATATRACKER_ENABLED
18
19#define METADATATRACKER_ONLY(s) (s)
20
21#include "winbase.h"
22#include "winwrap.h"
23#include "holder.h"
24#include "contract.h"
25#include <limits.h>
26#include <wchar.h>
27#include <stdio.h>
28#include "stdmacros.h"
29
30#include "metamodelpub.h"
31
32#define NUM_MD_SECTIONS (TBL_COUNT + MDPoolCount)
33
34#define STRING_POOL (TBL_COUNT + MDPoolStrings)
35#define GUID_POOL (TBL_COUNT + MDPoolGuids)
36#define BLOB_POOL (TBL_COUNT + MDPoolBlobs)
37#define USERSTRING_POOL (TBL_COUNT + MDPoolUSBlobs)
38
39class MetaDataTracker
40{
41 LPWSTR m_ModuleName;
42 BYTE *m_MetadataBase;
43 SIZE_T m_MetadataSize;
44 MetaDataTracker *m_next;
45
46 BYTE *m_mdSections[NUM_MD_SECTIONS];
47 SIZE_T m_mdSectionSize[NUM_MD_SECTIONS];
48 SIZE_T m_mdSectionRowSize[NUM_MD_SECTIONS];
49 BOOL m_bActivated;
50
51 static BOOL s_bEnabled;
52
53 static MetaDataTracker *m_MDTrackers;
54
55public:
56 // callback into IBCLogger.cpp. Done this crummy way because we can't include IBCLogger.h here nor link
57 // to IBCLogger.cpp
58 static void (*s_IBCLogMetaDataAccess)(const void *addr);
59 static void (*s_IBCLogMetaDataSearch)(const void *result);
60
61 MetaDataTracker(BYTE *baseAddress, DWORD mdSize, LPCWSTR modName)
62 {
63 CONTRACTL
64 {
65 CONSTRUCTOR_CHECK;
66 THROWS;
67 GC_NOTRIGGER;
68 INJECT_FAULT(ThrowOutOfMemory());
69 SO_INTOLERANT;
70 }
71 CONTRACTL_END;
72
73 m_ModuleName = NULL;
74
75 DWORD len = (DWORD)wcslen(modName);
76 _ASSERTE(len + 1 != 0); // Prevent Overflow
77 m_ModuleName = new wchar_t[len + 1];
78 NewArrayHolder<wchar_t> moduleNameHolder(m_ModuleName);
79 wcscpy_s((wchar_t *)m_ModuleName, len + 1, (wchar_t *)modName);
80
81 m_MetadataBase = baseAddress;
82 m_MetadataSize = mdSize;
83
84 m_next = m_MDTrackers;
85 m_MDTrackers = this;
86
87 memset (m_mdSections, 0, NUM_MD_SECTIONS*sizeof(BYTE*));
88 memset (m_mdSectionSize, 0, NUM_MD_SECTIONS*sizeof(SIZE_T));
89
90 moduleNameHolder.SuppressRelease();
91 }
92
93 ~MetaDataTracker()
94 {
95 CONTRACTL
96 {
97 DESTRUCTOR_CHECK;
98 NOTHROW;
99 GC_NOTRIGGER;
100 FORBID_FAULT;
101 SO_INTOLERANT;
102 }
103 CONTRACTL_END;
104
105 // Surely if we are dying, we are being deactivated as well
106 Deactivate();
107
108 if (m_ModuleName)
109 delete m_ModuleName;
110
111 // Remove this tracker from the global list of trackers
112
113 MetaDataTracker *mdMod = m_MDTrackers;
114
115 _ASSERTE (mdMod && "Trying to delete metadata tracker where none exist");
116
117 // If ours is the first tracker
118 if (mdMod == this)
119 {
120 m_MDTrackers = mdMod->m_next;
121 mdMod->m_next = NULL;
122 }
123 else
124 {
125 // Now traverse thru the list and maintain the prev ptr.
126 MetaDataTracker *mdModPrev = mdMod;
127 mdMod = mdMod->m_next;
128 while(mdMod)
129 {
130 if (mdMod == this)
131 {
132 mdModPrev->m_next = mdMod->m_next;
133 mdMod->m_next = NULL;
134 break;
135 }
136 mdModPrev = mdMod;
137 mdMod = mdMod->m_next;
138 }
139 }
140 }
141
142 static void Enable()
143 { LIMITED_METHOD_CONTRACT;
144 s_bEnabled = TRUE;
145 }
146
147 static void Disable()
148 { LIMITED_METHOD_CONTRACT;
149 s_bEnabled = FALSE;
150 }
151
152 static BOOL Enabled()
153 { LIMITED_METHOD_CONTRACT;
154 return s_bEnabled;
155 }
156
157 static void NoteSection(DWORD secNum, void *address, size_t size, size_t rowSize)
158 {
159 STATIC_CONTRACT_NOTHROW;
160 STATIC_CONTRACT_GC_NOTRIGGER;
161 STATIC_CONTRACT_SO_NOT_MAINLINE;
162
163 if (!Enabled())
164 return;
165
166 MetaDataTracker *mdMod = m_MDTrackers;
167 while( mdMod)
168 {
169 if (mdMod->NoteSectionInModule(secNum, address, size, rowSize))
170 return;
171
172 mdMod = mdMod->m_next;
173 }
174 }
175
176 // With logging disabled this quickly returns the address that was passed in
177 // this allows us to inline a smaller amount of code at callsites.
178 __forceinline static void* NoteAccess(void *address)
179 {
180 WRAPPER_NO_CONTRACT;
181
182 if (!Enabled())
183 return address;
184
185 return NoteAccessWorker(address);
186 }
187
188 __declspec(noinline) static void* NoteAccessWorker(void *address)
189 {
190 STATIC_CONTRACT_NOTHROW;
191 STATIC_CONTRACT_GC_NOTRIGGER;
192 STATIC_CONTRACT_SO_NOT_MAINLINE;
193
194 if (s_IBCLogMetaDataAccess != NULL)
195 s_IBCLogMetaDataAccess(address);
196
197 return address;
198 }
199
200 // See the comment above CMiniMdRW::GetHotMetadataTokensSearchAware
201 __forceinline static void NoteSearch(void *result)
202 {
203 WRAPPER_NO_CONTRACT;
204
205 if (!Enabled())
206 return;
207
208 NoteSearchWorker(result);
209 }
210
211 __declspec(noinline) static void NoteSearchWorker(void *result)
212 {
213 STATIC_CONTRACT_NOTHROW;
214 STATIC_CONTRACT_GC_NOTRIGGER;
215 STATIC_CONTRACT_SO_NOT_MAINLINE;
216
217 if (s_IBCLogMetaDataSearch != NULL && result != NULL)
218 s_IBCLogMetaDataSearch(result);
219 }
220
221 static MetaDataTracker * FindTracker(BYTE *_MDBaseAddress)
222 {
223 LIMITED_METHOD_CONTRACT;
224
225 if (!Enabled())
226 return NULL;
227
228 MetaDataTracker *mdMod = m_MDTrackers;
229 while( mdMod)
230 {
231 if (mdMod->m_MetadataBase == _MDBaseAddress)
232 return mdMod;
233
234 mdMod = mdMod->m_next;
235 }
236
237 return NULL;
238 }
239
240 void Activate()
241 {
242 LIMITED_METHOD_CONTRACT;
243
244 m_bActivated = TRUE;
245 }
246
247 void Deactivate()
248 {
249 LIMITED_METHOD_CONTRACT;
250
251 m_bActivated = FALSE;
252 }
253
254 BOOL IsActivated()
255 {
256 LIMITED_METHOD_CONTRACT;
257
258 return m_bActivated;
259 }
260
261 static MetaDataTracker *GetOrCreateMetaDataTracker (BYTE *baseAddress, DWORD mdSize, LPCWSTR modName)
262 {
263 CONTRACT(MetaDataTracker *)
264 {
265 THROWS;
266 GC_NOTRIGGER;
267 INJECT_FAULT(ThrowOutOfMemory());
268 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
269 SO_INTOLERANT;
270 }
271 CONTRACT_END;
272
273 MetaDataTracker *pTracker = NULL;
274
275 if (MetaDataTracker::Enabled())
276 {
277 pTracker = MetaDataTracker::FindTracker(baseAddress);
278 if (!pTracker)
279 {
280 FAULT_NOT_FATAL(); // It's ok - an OOM here is nonfatal
281 pTracker = new MetaDataTracker(baseAddress, mdSize, modName);
282 }
283 pTracker->Activate();
284 }
285
286 RETURN pTracker;
287 }
288
289 // Map a metadata address to a token for the purposes of the IBCLogger
290 static mdToken MapAddrToToken(const void *addr)
291 {
292 WRAPPER_NO_CONTRACT;
293
294 mdToken token = 0;
295 for (MetaDataTracker *mdMod = m_MDTrackers; mdMod; mdMod = mdMod->m_next)
296 {
297 token = mdMod->MapAddrToTokenInModule(addr);
298 if (token != 0)
299 break;
300 }
301 return token;
302 }
303
304
305private:
306
307 // ***************************************************************************
308 // Helper functions
309 // ***************************************************************************
310
311 BOOL NoteSectionInModule(DWORD secNum, void *address, size_t size, size_t rowSize)
312 {
313 WRAPPER_NO_CONTRACT;
314
315 PREFAST_ASSUME(secNum < NUM_MD_SECTIONS);
316
317 if (address < m_MetadataBase || address >= (m_MetadataBase + m_MetadataSize))
318 return FALSE;
319
320 // This address range belongs to us but the tracker is not activated.
321 if (!IsActivated())
322 {
323 // _ASSERTE (!"Metadata Tracker not active but trying to access metadata");
324 return TRUE;
325 }
326
327 m_mdSections[secNum] = (BYTE *)address;
328 m_mdSectionSize[secNum] = size;
329 m_mdSectionRowSize[secNum] = rowSize;
330
331 return TRUE;
332 }
333
334 // Map a metadata address to a fake token for the purposes of the IBCLogger
335 mdToken MapAddrToTokenInModule(const void *addr)
336 {
337 LIMITED_METHOD_CONTRACT;
338
339 if (!IsActivated())
340 return 0;
341
342 BYTE *address = (BYTE *)addr;
343
344 if (address < m_MetadataBase || address >= (m_MetadataBase + m_MetadataSize))
345 return 0;
346
347 for (DWORD secNum = 0; secNum < NUM_MD_SECTIONS; secNum++)
348 {
349 if ((address >= m_mdSections[secNum]) && (address < m_mdSections[secNum] + m_mdSectionSize[secNum]))
350 {
351 DWORD rid = (DWORD)((address - m_mdSections[secNum])/m_mdSectionRowSize[secNum]);
352 if (secNum < TBL_COUNT)
353 rid++;
354 return TokenFromRid(rid, (secNum<<24));
355 }
356 }
357 return 0;
358 }
359};
360
361#else // METADATATRACKER_ENABLED
362
363#define METADATATRACKER_ONLY(s)
364
365#endif // METADATATRACKER_ENABLED
366
367#endif // _METADATATRACKER_H_
368