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 DbgAppDomain_H
6#define DbgAppDomain_H
7
8// Forward declaration
9class AppDomain;
10
11
12// AppDomainInfo contains information about an AppDomain
13// All pointers are for the left side, and we do not own any of the memory
14struct AppDomainInfo
15{
16 ULONG m_id; // unique identifier
17 int m_iNameLengthInBytes;
18 LPCWSTR m_szAppDomainName;
19 AppDomain *m_pAppDomain; // only used by LS
20
21 // NOTE: These functions are just helpers and must not add a VTable
22 // to this struct (since we need to read this out-of-proc)
23
24 // Provide a clean definition of an empty entry
25 inline bool IsEmpty() const
26 {
27 return m_szAppDomainName == NULL;
28 }
29
30#ifndef RIGHT_SIDE_COMPILE
31 // Mark this entry as empty.
32 inline void FreeEntry()
33 {
34 m_szAppDomainName = NULL;
35 }
36
37 // Set the string name and length.
38 // If szName is null, it is adjusted to a global constant.
39 // This also causes the entry to be considered valid
40 inline void SetName(LPCWSTR szName)
41 {
42 if (szName != NULL)
43 m_szAppDomainName = szName;
44 else
45 m_szAppDomainName = W("<NoName>");
46
47 m_iNameLengthInBytes = (int) (wcslen(m_szAppDomainName) + 1) * sizeof(WCHAR);
48 }
49#endif
50};
51
52// Enforce the AppDomain IPC block binary layout doesn't change between versions.
53// Only an issue for x86 since that's the only platform w/ multiple versions.
54#if defined(DBG_TARGET_X86)
55static_assert_no_msg(offsetof(AppDomainInfo, m_id) == 0x0);
56static_assert_no_msg(offsetof(AppDomainInfo, m_iNameLengthInBytes) == 0x4);
57static_assert_no_msg(offsetof(AppDomainInfo, m_szAppDomainName) == 0x8);
58static_assert_no_msg(offsetof(AppDomainInfo, m_pAppDomain) == 0xc);
59#endif
60
61
62
63// The RemoteHANDLE encapsulates the PAL specific handling of handles to avoid PAL specific ifdefs
64// everywhere else in the code.
65// There are two common initialization patterns:
66//
67// 1. Publishing of local handle for other processes, the value of the wrapper is a local handle
68// in *this* process at the end:
69// - In this process, call SetLocal(hHandle) to initialize the handle.
70// - In the other processes, call DuplicateToLocalProcess to get a local copy of the handle.
71//
72// 2. Injecting of local handle into other process, the value of the wrapper is a local handle
73// in the *other* process at the end:
74// - In this process, call DuplicateToRemoteProcess(hProcess, hHandle) to initialize the handle.
75// - In the other process, call ImportToOtherProcess() to finish the initialization of the wrapper
76// with a local copy of the handle.
77//
78// Once initialized, the wrapper can be used the same way as a regular HANDLE in the process
79// it was initialized for. There is casting operator HANDLE to achieve that.
80
81struct RemoteHANDLE {
82 HANDLE m_hLocal;
83
84 operator HANDLE& ()
85 {
86 return m_hLocal;
87 }
88
89 void Close()
90 {
91 HANDLE hHandle = m_hLocal;
92 if (hHandle != NULL) {
93 m_hLocal = NULL;
94 CloseHandle(hHandle);
95 }
96 }
97
98 // Sets the local value of the handle. DuplicateToLocalProcess can be used later
99 // by the remote process to acquire the remote handle.
100 BOOL SetLocal(HANDLE hHandle)
101 {
102 m_hLocal = hHandle;
103 return TRUE;
104 }
105
106 // Duplicates the current handle value to remote process. ImportToLocalProcess
107 // should be called in the remote process before the handle is used in the remote process.
108 // NOTE: right now this is used for duplicating the debugger's process handle into the LS so
109 // that the LS can know when the RS has exited; thus we are only specifying SYNCHRONIZE
110 // access to mitigate any security concerns.
111 BOOL DuplicateToRemoteProcess(HANDLE hProcess, HANDLE hHandle)
112 {
113 return DuplicateHandle(GetCurrentProcess(), hHandle, hProcess, &m_hLocal,
114 SYNCHRONIZE, FALSE, 0);
115 }
116
117 // Duplicates the current handle value to local process. To be used in combination with SetLocal.
118 BOOL DuplicateToLocalProcess(HANDLE hProcess, HANDLE* pHandle)
119 {
120 return DuplicateHandle(hProcess, m_hLocal, GetCurrentProcess(), pHandle,
121 NULL, FALSE, DUPLICATE_SAME_ACCESS);
122 }
123
124 void CloseInRemoteProcess(HANDLE hProcess)
125 {
126 HANDLE hHandle = m_hLocal;
127 m_hLocal = NULL;
128
129 HANDLE hTmp;
130 if (DuplicateHandle(hProcess, hHandle, GetCurrentProcess(), &hTmp,
131 NULL, FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
132 {
133 CloseHandle(hTmp);
134 }
135 }
136
137 // Imports the handle to local process. To be used in combination with DuplicateToRemoteProcess.
138 HANDLE ImportToLocalProcess()
139 {
140 return m_hLocal;
141 }
142};
143
144
145// AppDomain publishing server support:
146// Information about all appdomains in the process will be maintained
147// in the shared memory block for use by the debugger, etc.
148// This structure defines the layout of the information that will
149// be maintained.
150struct AppDomainEnumerationIPCBlock
151{
152 // !!! The binary format of this layout must remain the same across versions so that
153 // !!! a V2.0 publisher can inspect a v1.0 app.
154
155 // lock for serialization while manipulating AppDomain list.
156 RemoteHANDLE m_hMutex;
157
158 // Number of slots in AppDomainListElement array
159 int m_iTotalSlots;
160 int m_iNumOfUsedSlots;
161 int m_iLastFreedSlot;
162 int m_iSizeInBytes; // Size of AppDomainInfo in bytes
163
164 // We can use psapi!GetModuleFileNameEx to get the module name.
165 // This provides an alternative.
166 int m_iProcessNameLengthInBytes;
167 WCHAR *m_szProcessName;
168
169 AppDomainInfo *m_rgListOfAppDomains;
170 BOOL m_fLockInvalid;
171
172
173#ifndef RIGHT_SIDE_COMPILE
174 /*************************************************************************
175 * Locks the list
176 *************************************************************************/
177 BOOL Lock()
178 {
179 DWORD dwRes = WaitForSingleObject(m_hMutex, 3000);
180 if (dwRes == WAIT_TIMEOUT)
181 {
182 // Nobody should get stuck holding this lock.
183 // If we timeout on the wait, then either:
184 // - it's a really bad race and somebody got preempted for a long time
185 // - perhaps somebody's doing a DOS attack and holding onto the mutex.
186 m_fLockInvalid = TRUE;
187 }
188
189
190 // The only time this can happen is if we're in shutdown and a thread
191 // that held this lock is killed. If this happens, assume that this
192 // IPC block is in an invalid state and return FALSE to indicate
193 // that people shouldn't do anything with the block anymore.
194 if (dwRes == WAIT_ABANDONED)
195 {
196 m_fLockInvalid = TRUE;
197 }
198
199 if (m_fLockInvalid)
200 {
201 Unlock();
202 }
203
204 return (dwRes == WAIT_OBJECT_0 && !m_fLockInvalid);
205 }
206
207 /*************************************************************************
208 * Unlocks the list
209 *************************************************************************/
210 void Unlock()
211 {
212 // Lock may or may not be valid at this point. Thus Release may fail,
213 // but we'll just ignore that.
214 ReleaseMutex(m_hMutex);
215 }
216
217 /*************************************************************************
218 * Gets a free AppDomainInfo entry, and will allocate room if there are
219 * no free slots left.
220 *************************************************************************/
221 AppDomainInfo *GetFreeEntry()
222 {
223 // first check to see if there is space available. If not, then realloc.
224 if (m_iNumOfUsedSlots == m_iTotalSlots)
225 {
226 // need to realloc
227 AppDomainInfo *pTemp =
228 new (nothrow) AppDomainInfo [m_iTotalSlots*2];
229
230 if (pTemp == NULL)
231 {
232 return (NULL);
233 }
234
235 memcpy (pTemp, m_rgListOfAppDomains, m_iSizeInBytes);
236
237 delete [] m_rgListOfAppDomains;
238
239 // Initialize the increased portion of the realloced memory
240 int iNewSlotSize = m_iTotalSlots * 2;
241
242 for (int iIndex = m_iTotalSlots; iIndex < iNewSlotSize; iIndex++)
243 pTemp[iIndex].FreeEntry();
244
245 m_rgListOfAppDomains = pTemp;
246 m_iTotalSlots = iNewSlotSize;
247 m_iSizeInBytes *= 2;
248 }
249
250 // Walk the list looking for an empty slot. Start from the last
251 // one which was freed.
252 {
253 int i = m_iLastFreedSlot;
254
255 do
256 {
257 // Pointer to the entry being examined
258 AppDomainInfo *pADInfo = &(m_rgListOfAppDomains[i]);
259
260 // is the slot available?
261 if (pADInfo->IsEmpty())
262 return (pADInfo);
263
264 i = (i + 1) % m_iTotalSlots;
265
266 } while (i != m_iLastFreedSlot);
267 }
268
269 _ASSERTE(!"ADInfo::GetFreeEntry: should never get here.");
270 return (NULL);
271 }
272
273 /*************************************************************************
274 * Returns an AppDomainInfo slot to the free list.
275 *************************************************************************/
276 void FreeEntry(AppDomainInfo *pADInfo)
277 {
278 _ASSERTE(pADInfo >= m_rgListOfAppDomains &&
279 pADInfo < m_rgListOfAppDomains + m_iSizeInBytes);
280 _ASSERTE(((size_t)pADInfo - (size_t)m_rgListOfAppDomains) %
281 sizeof(AppDomainInfo) == 0);
282
283 // Mark this slot as free
284 pADInfo->FreeEntry();
285
286#ifdef _DEBUG
287 memset(pADInfo, 0, sizeof(AppDomainInfo));
288#endif
289
290 // decrement the used slot count
291 m_iNumOfUsedSlots--;
292
293 // Save the last freed slot.
294 m_iLastFreedSlot = (int)((size_t)pADInfo - (size_t)m_rgListOfAppDomains) /
295 sizeof(AppDomainInfo);
296 }
297
298 /*************************************************************************
299 * Finds an AppDomainInfo entry corresponding to the AppDomain pointer.
300 * Returns NULL if no such entry exists.
301 *************************************************************************/
302 AppDomainInfo *FindEntry(AppDomain *pAD)
303 {
304 // Walk the list looking for a matching entry
305 for (int i = 0; i < m_iTotalSlots; i++)
306 {
307 AppDomainInfo *pADInfo = &(m_rgListOfAppDomains[i]);
308
309 if (!pADInfo->IsEmpty() &&
310 pADInfo->m_pAppDomain == pAD)
311 return pADInfo;
312 }
313
314 return (NULL);
315 }
316
317 /*************************************************************************
318 * Returns the first AppDomainInfo entry in the list. Returns NULL if
319 * no such entry exists.
320 *************************************************************************/
321 AppDomainInfo *FindFirst()
322 {
323 // Walk the list looking for a non-empty entry
324 for (int i = 0; i < m_iTotalSlots; i++)
325 {
326 AppDomainInfo *pADInfo = &(m_rgListOfAppDomains[i]);
327
328 if (!pADInfo->IsEmpty())
329 return pADInfo;
330 }
331
332 return (NULL);
333 }
334
335 /*************************************************************************
336 * Returns the next AppDomainInfo entry after pADInfo. Returns NULL if
337 * pADInfo was the last in the list.
338 *************************************************************************/
339 AppDomainInfo *FindNext(AppDomainInfo *pADInfo)
340 {
341 _ASSERTE(pADInfo >= m_rgListOfAppDomains &&
342 pADInfo < m_rgListOfAppDomains + m_iSizeInBytes);
343 _ASSERTE(((size_t)pADInfo - (size_t)m_rgListOfAppDomains) %
344 sizeof(AppDomainInfo) == 0);
345
346 // Walk the list looking for the next non-empty entry
347 for (int i = (int)((size_t)pADInfo - (size_t)m_rgListOfAppDomains)
348 / sizeof(AppDomainInfo) + 1;
349 i < m_iTotalSlots;
350 i++)
351 {
352 AppDomainInfo *pADInfoTemp = &(m_rgListOfAppDomains[i]);
353
354 if (!pADInfoTemp->IsEmpty())
355 return pADInfoTemp;
356 }
357
358 return (NULL);
359 }
360#endif // RIGHT_SIDE_COMPILE
361};
362
363// Enforce the AppDomain IPC block binary layout doesn't change between versions.
364// Only an issue for x86 since that's the only platform w/ multiple versions.
365#if defined(DBG_TARGET_X86)
366static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_hMutex) == 0x0);
367static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_iTotalSlots) == 0x4);
368static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_iNumOfUsedSlots) == 0x8);
369static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_iLastFreedSlot) == 0xc);
370static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_iSizeInBytes) == 0x10);
371static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_iProcessNameLengthInBytes) == 0x14);
372static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_szProcessName) == 0x18);
373static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_rgListOfAppDomains) == 0x1c);
374static_assert_no_msg(offsetof(AppDomainEnumerationIPCBlock, m_fLockInvalid) == 0x20);
375#endif
376
377#endif //DbgAppDomain_H
378
379
380
381