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: DebuggerModule.cpp |
6 | // |
7 | |
8 | // |
9 | // Stuff for tracking DebuggerModules. |
10 | // |
11 | //***************************************************************************** |
12 | |
13 | #include "stdafx.h" |
14 | #include "../inc/common.h" |
15 | #include "perflog.h" |
16 | #include "eeconfig.h" // This is here even for retail & free builds... |
17 | #include "vars.hpp" |
18 | #include <limits.h> |
19 | #include "ilformatter.h" |
20 | #include "debuginfostore.h" |
21 | |
22 | |
23 | /* ------------------------------------------------------------------------ * |
24 | * Debugger Module routines |
25 | * ------------------------------------------------------------------------ */ |
26 | |
27 | // <TODO> (8/12/2002) |
28 | // We need to stop lying to the debugger about not sharing Modules. |
29 | // Primary Modules allow a transition to that. Once we stop lying, |
30 | // then all modules will be their own Primary. |
31 | // </TODO> |
32 | // Select the primary module. |
33 | // Primary Modules are selected DebuggerModules that map 1:1 w/ Module*. |
34 | // If the runtime module is not shared, then we're our own Primary Module. |
35 | // If the Runtime module is shared, the primary module is some specific instance. |
36 | // Note that a domain-neutral module can be loaded into multiple domains without |
37 | // being loaded into the default domain, and so there is no "primary module" as far |
38 | // as the CLR is concerned - we just pick any one and call it primary. |
39 | void DebuggerModule::PickPrimaryModule() |
40 | { |
41 | CONTRACTL |
42 | { |
43 | SO_NOT_MAINLINE; |
44 | NOTHROW; |
45 | GC_NOTRIGGER; |
46 | } |
47 | CONTRACTL_END; |
48 | |
49 | Debugger::DebuggerDataLockHolder ch(g_pDebugger); |
50 | |
51 | LOG((LF_CORDB, LL_INFO100000, "DM::PickPrimaryModule, this=0x%p\n" , this)); |
52 | |
53 | // We're our own primary module, unless something else proves otherwise. |
54 | // Note that we should be able to skip all of this if this module is not domain neutral |
55 | m_pPrimaryModule = this; |
56 | |
57 | // This should be thread safe because our creation for the DebuggerModules |
58 | // are serialized. |
59 | |
60 | // Lookup our Runtime Module. If it's already in there, |
61 | // then |
62 | DebuggerModuleTable * pTable = g_pDebugger->GetModuleTable(); |
63 | |
64 | // If the table doesn't exist yet, then we must be a primary module. |
65 | if (pTable == NULL) |
66 | { |
67 | LOG((LF_CORDB, LL_INFO100000, "DM::PickPrimaryModule, this=0x%p, table not created yet\n" , this)); |
68 | return; |
69 | } |
70 | |
71 | // Look through existing module list to find a common primary DebuggerModule |
72 | // for the given EE Module. We don't know what order we'll traverse in. |
73 | |
74 | HASHFIND f; |
75 | for (DebuggerModule * m = pTable->GetFirstModule(&f); |
76 | m != NULL; |
77 | m = pTable->GetNextModule(&f)) |
78 | { |
79 | |
80 | if (m->GetRuntimeModule() == this->GetRuntimeModule()) |
81 | { |
82 | // Make sure we're picking another primary module. |
83 | _ASSERTE(m->GetPrimaryModule() != m); |
84 | } |
85 | } // end for |
86 | |
87 | // If we got here, then this instance is a Primary Module. |
88 | LOG((LF_CORDB, LL_INFO100000, "DM::PickPrimaryModule, this=%p is first, primary.\n" , this)); |
89 | } |
90 | |
91 | void DebuggerModule::SetCanChangeJitFlags(bool fCanChangeJitFlags) |
92 | { |
93 | m_fCanChangeJitFlags = fCanChangeJitFlags; |
94 | } |
95 | |
96 | #ifndef DACCESS_COMPILE |
97 | |
98 | |
99 | DebuggerModuleTable::DebuggerModuleTable() : CHashTableAndData<CNewZeroData>(101) |
100 | { |
101 | WRAPPER_NO_CONTRACT; |
102 | |
103 | SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; |
104 | NewInit(101, sizeof(DebuggerModuleEntry), 101); |
105 | } |
106 | |
107 | DebuggerModuleTable::~DebuggerModuleTable() |
108 | { |
109 | WRAPPER_NO_CONTRACT; |
110 | |
111 | _ASSERTE(ThreadHoldsLock()); |
112 | Clear(); |
113 | } |
114 | |
115 | |
116 | #ifdef _DEBUG |
117 | bool DebuggerModuleTable::ThreadHoldsLock() |
118 | { |
119 | // In shutdown (g_fProcessDetach), the shutdown thread implicitly holds all locks. |
120 | return g_fProcessDetach || g_pDebugger->HasDebuggerDataLock(); |
121 | } |
122 | #endif |
123 | |
124 | // |
125 | // RemoveModules removes any module loaded into the given appdomain from the hash. This is used when we send an |
126 | // ExitAppdomain event to ensure that there are no leftover modules in the hash. This can happen when we have shared |
127 | // modules that aren't properly accounted for in the CLR. We miss sending UnloadModule events for those modules, so |
128 | // we clean them up with this method. |
129 | // |
130 | void DebuggerModuleTable::RemoveModules(AppDomain *pAppDomain) |
131 | { |
132 | CONTRACTL |
133 | { |
134 | NOTHROW; |
135 | GC_NOTRIGGER; |
136 | } |
137 | CONTRACTL_END; |
138 | |
139 | LOG((LF_CORDB, LL_INFO1000, "DMT::RM removing all modules from AD 0x%08x\n" , pAppDomain)); |
140 | |
141 | _ASSERTE(ThreadHoldsLock()); |
142 | |
143 | HASHFIND hf; |
144 | DebuggerModuleEntry *pDME = (DebuggerModuleEntry *) FindFirstEntry(&hf); |
145 | |
146 | while (pDME != NULL) |
147 | { |
148 | DebuggerModule *pDM = pDME->module; |
149 | |
150 | if (pDM->GetAppDomain() == pAppDomain) |
151 | { |
152 | LOG((LF_CORDB, LL_INFO1000, "DMT::RM removing DebuggerModule 0x%08x\n" , pDM)); |
153 | |
154 | // Defer to the normal logic in RemoveModule for the actual removal. This accurately simulates what |
155 | // happens when we process an UnloadModule event. |
156 | RemoveModule(pDM->GetRuntimeModule(), pAppDomain); |
157 | |
158 | // Start back at the first entry since we just modified the hash. |
159 | pDME = (DebuggerModuleEntry *) FindFirstEntry(&hf); |
160 | } |
161 | else |
162 | { |
163 | pDME = (DebuggerModuleEntry *) FindNextEntry(&hf); |
164 | } |
165 | } |
166 | |
167 | LOG((LF_CORDB, LL_INFO1000, "DMT::RM done removing all modules from AD 0x%08x\n" , pAppDomain)); |
168 | } |
169 | |
170 | void DebuggerModuleTable::Clear() |
171 | { |
172 | CONTRACTL |
173 | { |
174 | NOTHROW; |
175 | GC_NOTRIGGER; |
176 | } |
177 | CONTRACTL_END; |
178 | |
179 | _ASSERTE(ThreadHoldsLock()); |
180 | |
181 | HASHFIND hf; |
182 | DebuggerModuleEntry *pDME; |
183 | |
184 | pDME = (DebuggerModuleEntry *) FindFirstEntry(&hf); |
185 | |
186 | while (pDME) |
187 | { |
188 | DebuggerModule *pDM = pDME->module; |
189 | Module *pEEM = pDM->GetRuntimeModule(); |
190 | |
191 | TRACE_FREE(pDME->module); |
192 | DeleteInteropSafe(pDM); |
193 | Delete(HASH(pEEM), (HASHENTRY *) pDME); |
194 | |
195 | pDME = (DebuggerModuleEntry *) FindFirstEntry(&hf); |
196 | } |
197 | |
198 | CHashTableAndData<CNewZeroData>::Clear(); |
199 | } |
200 | |
201 | void DebuggerModuleTable::AddModule(DebuggerModule *pModule) |
202 | { |
203 | CONTRACTL |
204 | { |
205 | THROWS; |
206 | GC_NOTRIGGER; |
207 | } |
208 | CONTRACTL_END; |
209 | |
210 | _ASSERTE(ThreadHoldsLock()); |
211 | |
212 | _ASSERTE(pModule != NULL); |
213 | |
214 | LOG((LF_CORDB, LL_EVERYTHING, "DMT::AM: DebuggerMod:0x%x Module:0x%x AD:0x%x\n" , |
215 | pModule, pModule->GetRuntimeModule(), pModule->GetAppDomain())); |
216 | |
217 | DebuggerModuleEntry * pEntry = (DebuggerModuleEntry *) Add(HASH(pModule->GetRuntimeModule())); |
218 | if (pEntry == NULL) |
219 | { |
220 | ThrowOutOfMemory(); |
221 | } |
222 | |
223 | pEntry->module = pModule; |
224 | |
225 | // Don't need to update the primary module since it was set when we created the module. |
226 | _ASSERTE(pModule->GetPrimaryModule() != NULL); |
227 | } |
228 | |
229 | //----------------------------------------------------------------------------- |
230 | // Remove a DebuggerModule from the module table. |
231 | // This occurs in response to AppDomain unload. |
232 | // Note that this doesn't necessarily mean the EE Module is being unloaded (it may be shared) |
233 | //----------------------------------------------------------------------------- |
234 | void DebuggerModuleTable::RemoveModule(Module* module, AppDomain *pAppDomain) |
235 | { |
236 | CONTRACTL |
237 | { |
238 | NOTHROW; |
239 | GC_NOTRIGGER; |
240 | } |
241 | CONTRACTL_END; |
242 | |
243 | // If this is a domain neutral module, then scan the complete list of DebuggerModules looking |
244 | // for the one with a matching appdomain id. |
245 | // Note: we have to make sure to lookup the module with the app domain parameter if the module lives in a shared |
246 | // assembly or the system assembly. <BUGNUM>Bugs 65943 & 81728.</BUGNUM> |
247 | _ASSERTE( FALSE ); |
248 | |
249 | } |
250 | |
251 | |
252 | #endif // DACCESS_COMPILE |
253 | |
254 | DebuggerModule *DebuggerModuleTable::GetModule(Module* module) |
255 | { |
256 | CONTRACTL |
257 | { |
258 | NOTHROW; |
259 | GC_NOTRIGGER; |
260 | } |
261 | CONTRACTL_END; |
262 | |
263 | _ASSERTE(module != NULL); |
264 | _ASSERTE(ThreadHoldsLock()); |
265 | |
266 | DebuggerModuleEntry *entry |
267 | = (DebuggerModuleEntry *) Find(HASH(module), KEY(module)); |
268 | if (entry == NULL) |
269 | return NULL; |
270 | else |
271 | return entry->module; |
272 | } |
273 | |
274 | // We should never look for a NULL Module * |
275 | DebuggerModule *DebuggerModuleTable::GetModule(Module* module, AppDomain* pAppDomain) |
276 | { |
277 | CONTRACTL |
278 | { |
279 | NOTHROW; |
280 | GC_NOTRIGGER; |
281 | } |
282 | CONTRACTL_END; |
283 | |
284 | _ASSERTE(module != NULL); |
285 | _ASSERTE(ThreadHoldsLock()); |
286 | |
287 | |
288 | HASHFIND findmodule; |
289 | DebuggerModuleEntry *moduleentry; |
290 | |
291 | for (moduleentry = (DebuggerModuleEntry*) FindFirstEntry(&findmodule); |
292 | moduleentry != NULL; |
293 | moduleentry = (DebuggerModuleEntry*) FindNextEntry(&findmodule)) |
294 | { |
295 | DebuggerModule *pModule = moduleentry->module; |
296 | |
297 | if ((pModule->GetRuntimeModule() == module) && |
298 | (pModule->GetAppDomain() == pAppDomain)) |
299 | return pModule; |
300 | } |
301 | |
302 | // didn't find any match! So return a matching module for any app domain |
303 | return NULL; |
304 | } |
305 | |
306 | DebuggerModule *DebuggerModuleTable::GetFirstModule(HASHFIND *info) |
307 | { |
308 | CONTRACTL |
309 | { |
310 | NOTHROW; |
311 | GC_NOTRIGGER; |
312 | } |
313 | CONTRACTL_END; |
314 | |
315 | _ASSERTE(ThreadHoldsLock()); |
316 | |
317 | DebuggerModuleEntry *entry = (DebuggerModuleEntry *) FindFirstEntry(info); |
318 | if (entry == NULL) |
319 | return NULL; |
320 | else |
321 | return entry->module; |
322 | } |
323 | |
324 | DebuggerModule *DebuggerModuleTable::GetNextModule(HASHFIND *info) |
325 | { |
326 | CONTRACTL |
327 | { |
328 | NOTHROW; |
329 | GC_NOTRIGGER; |
330 | } |
331 | CONTRACTL_END; |
332 | |
333 | _ASSERTE(ThreadHoldsLock()); |
334 | |
335 | DebuggerModuleEntry *entry = (DebuggerModuleEntry *) FindNextEntry(info); |
336 | if (entry == NULL) |
337 | return NULL; |
338 | else |
339 | return entry->module; |
340 | } |
341 | |
342 | |
343 | |