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 | #include "stdafx.h" |
6 | #include "dbgtransportsession.h" |
7 | #include "dbgtransportmanager.h" |
8 | #include "coreclrremotedebugginginterfaces.h" |
9 | |
10 | |
11 | |
12 | #ifdef FEATURE_DBGIPC_TRANSPORT_DI |
13 | |
14 | DbgTransportTarget *g_pDbgTransportTarget = NULL; |
15 | |
16 | DbgTransportTarget::DbgTransportTarget() |
17 | { |
18 | memset(this, 0, sizeof(*this)); |
19 | } |
20 | |
21 | // Initialization routine called only by the DbgTransportManager. |
22 | HRESULT DbgTransportTarget::Init() |
23 | { |
24 | m_sLock.Init("DbgTransportTarget Lock" , RSLock::cLockFlat, RSLock::LL_DBG_TRANSPORT_TARGET_LOCK); |
25 | |
26 | return S_OK; |
27 | } |
28 | |
29 | // Shutdown routine called only by the DbgTransportManager. |
30 | void DbgTransportTarget::Shutdown() |
31 | { |
32 | DbgTransportLog(LC_Always, "DbgTransportTarget shutting down" ); |
33 | |
34 | { |
35 | RSLockHolder lock(&m_sLock); |
36 | while (m_pProcessList) |
37 | { |
38 | ProcessEntry *pDelProcess = m_pProcessList; |
39 | m_pProcessList = m_pProcessList->m_pNext; |
40 | delete pDelProcess; |
41 | } |
42 | } |
43 | m_sLock.Destroy(); |
44 | } |
45 | |
46 | |
47 | // Given a PID attempt to find or create a DbgTransportSession instance to manage a connection to a runtime in |
48 | // that process. Returns E_UNEXPECTED if the process can't be found. Also returns a handle that can be waited |
49 | // on for process termination. |
50 | HRESULT DbgTransportTarget::GetTransportForProcess(const ProcessDescriptor *pProcessDescriptor, |
51 | DbgTransportSession **ppTransport, |
52 | HANDLE *phProcessHandle) |
53 | { |
54 | RSLockHolder lock(&m_sLock); |
55 | HRESULT hr = S_OK; |
56 | DWORD dwPID = pProcessDescriptor->m_Pid; |
57 | |
58 | ProcessEntry *entry = LocateProcessByPID(dwPID); |
59 | |
60 | if (entry == NULL) |
61 | { |
62 | |
63 | NewHolder<ProcessEntry> newEntry = new(nothrow) ProcessEntry(); |
64 | if (newEntry == NULL) |
65 | return E_OUTOFMEMORY; |
66 | |
67 | NewHolder<DbgTransportSession> transport = new(nothrow) DbgTransportSession(); |
68 | if (transport == NULL) |
69 | { |
70 | return E_OUTOFMEMORY; |
71 | } |
72 | |
73 | |
74 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID); |
75 | if (hProcess == NULL) |
76 | { |
77 | transport->Shutdown(); |
78 | return HRESULT_FROM_GetLastError(); |
79 | } |
80 | |
81 | // Initialize it (this immediately starts the remote connection process). |
82 | hr = transport->Init(*pProcessDescriptor, hProcess); |
83 | if (FAILED(hr)) |
84 | { |
85 | transport->Shutdown(); |
86 | CloseHandle(hProcess); |
87 | return hr; |
88 | } |
89 | |
90 | entry = newEntry; |
91 | newEntry.SuppressRelease(); |
92 | entry->m_dwPID = dwPID; |
93 | entry->m_hProcess = hProcess; |
94 | entry->m_transport = transport; |
95 | transport.SuppressRelease(); |
96 | entry->m_cProcessRef = 0; |
97 | |
98 | // Adding new entry to the list. |
99 | entry->m_pNext = m_pProcessList; |
100 | m_pProcessList = entry; |
101 | } |
102 | |
103 | entry->m_cProcessRef++; |
104 | _ASSERTE(entry->m_cProcessRef > 0); |
105 | _ASSERTE(entry->m_transport != NULL); |
106 | _ASSERTE((intptr_t)entry->m_hProcess > 0); |
107 | |
108 | *ppTransport = entry->m_transport; |
109 | if (!DuplicateHandle(GetCurrentProcess(), |
110 | entry->m_hProcess, |
111 | GetCurrentProcess(), |
112 | phProcessHandle, |
113 | 0, // ignored since we are going to pass DUPLICATE_SAME_ACCESS |
114 | FALSE, |
115 | DUPLICATE_SAME_ACCESS)) |
116 | { |
117 | return HRESULT_FROM_GetLastError(); |
118 | } |
119 | |
120 | return hr; |
121 | } |
122 | |
123 | |
124 | // Release another reference to the transport associated with dwPID. Once all references are gone (modulo the |
125 | // manager's own weak reference) clean up the transport and deallocate it. |
126 | void DbgTransportTarget::ReleaseTransport(DbgTransportSession *pTransport) |
127 | { |
128 | RSLockHolder lock(&m_sLock); |
129 | |
130 | ProcessEntry *entry = m_pProcessList; |
131 | |
132 | // Pointer to the pointer that points to *entry. |
133 | // It either points to m_pProcessList or m_pNext of some entry. |
134 | // It is used to fix the linked list after deletion of an entry. |
135 | ProcessEntry **prevPtr = &m_pProcessList; |
136 | |
137 | // Looking for ProcessEntry with a given transport |
138 | while (entry) |
139 | { |
140 | |
141 | _ASSERTE(entry->m_cProcessRef > 0); |
142 | _ASSERTE(entry->m_transport != NULL); |
143 | _ASSERTE((intptr_t)entry->m_hProcess > 0); |
144 | |
145 | if (entry->m_transport == pTransport) |
146 | { |
147 | // Mark that it has one less holder now |
148 | entry->m_cProcessRef--; |
149 | |
150 | // If no more holders remove the entry from the list and free resources |
151 | if (entry->m_cProcessRef == 0) |
152 | { |
153 | *prevPtr = entry->m_pNext; |
154 | delete entry; |
155 | } |
156 | return; |
157 | } |
158 | prevPtr = &entry->m_pNext; |
159 | entry = entry->m_pNext; |
160 | } |
161 | |
162 | _ASSERTE(!"Trying to release transport that doesn't belong to this DbgTransportTarget" ); |
163 | pTransport->Shutdown(); |
164 | } |
165 | |
166 | HRESULT DbgTransportTarget::CreateProcess(LPCWSTR lpApplicationName, |
167 | LPCWSTR lpCommandLine, |
168 | LPSECURITY_ATTRIBUTES lpProcessAttributes, |
169 | LPSECURITY_ATTRIBUTES lpThreadAttributes, |
170 | BOOL bInheritHandles, |
171 | DWORD dwCreationFlags, |
172 | LPVOID lpEnvironment, |
173 | LPCWSTR lpCurrentDirectory, |
174 | LPSTARTUPINFOW lpStartupInfo, |
175 | LPPROCESS_INFORMATION lpProcessInformation) |
176 | { |
177 | |
178 | BOOL result = WszCreateProcess(lpApplicationName, |
179 | lpCommandLine, |
180 | lpProcessAttributes, |
181 | lpThreadAttributes, |
182 | bInheritHandles, |
183 | dwCreationFlags, |
184 | lpEnvironment, |
185 | lpCurrentDirectory, |
186 | lpStartupInfo, |
187 | lpProcessInformation); |
188 | |
189 | if (!result) |
190 | { |
191 | return HRESULT_FROM_GetLastError(); |
192 | } |
193 | |
194 | return S_OK; |
195 | } |
196 | |
197 | // Kill the process identified by PID. |
198 | void DbgTransportTarget::KillProcess(DWORD dwPID) |
199 | { |
200 | HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwPID); |
201 | if (hProcess != NULL) |
202 | { |
203 | TerminateProcess(hProcess, 0); |
204 | CloseHandle(hProcess); |
205 | } |
206 | } |
207 | |
208 | DbgTransportTarget::ProcessEntry::~ProcessEntry() |
209 | { |
210 | CloseHandle(m_hProcess); |
211 | m_hProcess = NULL; |
212 | |
213 | m_transport->Shutdown(); |
214 | m_transport = NULL; |
215 | } |
216 | |
217 | // Locate a process entry by PID. Assumes the lock is already held. |
218 | DbgTransportTarget::ProcessEntry *DbgTransportTarget::LocateProcessByPID(DWORD dwPID) |
219 | { |
220 | _ASSERTE(m_sLock.HasLock()); |
221 | |
222 | ProcessEntry *pProcess = m_pProcessList; |
223 | while (pProcess) |
224 | { |
225 | if (pProcess->m_dwPID == dwPID) |
226 | return pProcess; |
227 | pProcess = pProcess->m_pNext; |
228 | } |
229 | return NULL; |
230 | } |
231 | |
232 | #endif // FEATURE_DBGIPC_TRANSPORT_DI |
233 | |