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.
39void 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
91void DebuggerModule::SetCanChangeJitFlags(bool fCanChangeJitFlags)
92{
93 m_fCanChangeJitFlags = fCanChangeJitFlags;
94}
95
96#ifndef DACCESS_COMPILE
97
98
99DebuggerModuleTable::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
107DebuggerModuleTable::~DebuggerModuleTable()
108{
109 WRAPPER_NO_CONTRACT;
110
111 _ASSERTE(ThreadHoldsLock());
112 Clear();
113}
114
115
116#ifdef _DEBUG
117bool 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//
130void 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
170void 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
201void 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//-----------------------------------------------------------------------------
234void 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
254DebuggerModule *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 *
275DebuggerModule *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
306DebuggerModule *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
324DebuggerModule *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