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 | |
39 | class 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 | |
55 | public: |
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 | |
305 | private: |
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 | |