- DebugSetProcessKillOnExit: Use SetLastErrorByStatus for NTSTATUS codes
[reactos.git] / reactos / dll / win32 / kernel32 / debug / debugger.c
1 /*
2 * PROJECT: ReactOS Win32 Base API
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/kernel32/debug/debugger.c
5 * PURPOSE: Wrappers for the NT Debug Implementation
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include <k32.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 typedef struct _DBGSS_THREAD_DATA
16 {
17 struct _DBGSS_THREAD_DATA *Next;
18 HANDLE ThreadHandle;
19 HANDLE ProcessHandle;
20 DWORD ProcessId;
21 DWORD ThreadId;
22 BOOLEAN HandleMarked;
23 } DBGSS_THREAD_DATA, *PDBGSS_THREAD_DATA;
24
25 #define DbgSsSetThreadData(d) \
26 NtCurrentTeb()->DbgSsReserved[0] = d
27
28 #define DbgSsGetThreadData() \
29 ((PDBGSS_THREAD_DATA)NtCurrentTeb()->DbgSsReserved[0])
30
31 /* PRIVATE FUNCTIONS *********************************************************/
32
33 VOID
34 WINAPI
35 SaveThreadHandle(IN DWORD dwProcessId,
36 IN DWORD dwThreadId,
37 IN HANDLE hThread)
38 {
39 PDBGSS_THREAD_DATA ThreadData;
40
41 /* Allocate a thread structure */
42 ThreadData = RtlAllocateHeap(RtlGetProcessHeap(),
43 0,
44 sizeof(DBGSS_THREAD_DATA));
45 if (!ThreadData) return;
46
47 /* Fill it out */
48 ThreadData->ThreadHandle = hThread;
49 ThreadData->ProcessId = dwProcessId;
50 ThreadData->ThreadId = dwThreadId;
51 ThreadData->ProcessHandle = NULL;
52 ThreadData->HandleMarked = FALSE;
53
54 /* Link it */
55 ThreadData->Next = DbgSsGetThreadData();
56 DbgSsSetThreadData(ThreadData);
57 }
58
59 VOID
60 WINAPI
61 SaveProcessHandle(IN DWORD dwProcessId,
62 IN HANDLE hProcess)
63 {
64 PDBGSS_THREAD_DATA ThreadData;
65
66 /* Allocate a thread structure */
67 ThreadData = RtlAllocateHeap(RtlGetProcessHeap(),
68 0,
69 sizeof(DBGSS_THREAD_DATA));
70 if (!ThreadData) return;
71
72 /* Fill it out */
73 ThreadData->ProcessHandle = hProcess;
74 ThreadData->ProcessId = dwProcessId;
75 ThreadData->ThreadId = 0;
76 ThreadData->ThreadHandle = NULL;
77 ThreadData->HandleMarked = FALSE;
78
79 /* Link it */
80 ThreadData->Next = DbgSsGetThreadData();
81 DbgSsSetThreadData(ThreadData);
82 }
83
84 VOID
85 WINAPI
86 MarkThreadHandle(IN DWORD dwThreadId)
87 {
88 PDBGSS_THREAD_DATA ThreadData;
89
90 /* Loop all thread data events */
91 ThreadData = DbgSsGetThreadData();
92 while (ThreadData)
93 {
94 /* Check if this one matches */
95 if (ThreadData->ThreadId == dwThreadId)
96 {
97 /* Mark the structure and break out */
98 ThreadData->HandleMarked = TRUE;
99 break;
100 }
101
102 /* Move to the next one */
103 ThreadData = ThreadData->Next;
104 }
105 }
106
107 VOID
108 WINAPI
109 MarkProcessHandle(IN DWORD dwProcessId)
110 {
111 PDBGSS_THREAD_DATA ThreadData;
112
113 /* Loop all thread data events */
114 ThreadData = DbgSsGetThreadData();
115 while (ThreadData)
116 {
117 /* Check if this one matches */
118 if (ThreadData->ProcessId == dwProcessId)
119 {
120 /* Make sure the thread ID is empty */
121 if (!ThreadData->ThreadId)
122 {
123 /* Mark the structure and break out */
124 ThreadData->HandleMarked = TRUE;
125 break;
126 }
127 }
128
129 /* Move to the next one */
130 ThreadData = ThreadData->Next;
131 }
132 }
133
134 VOID
135 WINAPI
136 RemoveHandles(IN DWORD dwProcessId,
137 IN DWORD dwThreadId)
138 {
139 PDBGSS_THREAD_DATA ThreadData;
140
141 /* Loop all thread data events */
142 ThreadData = DbgSsGetThreadData();
143 while (ThreadData)
144 {
145 /* Check if this one matches */
146 if (ThreadData->ProcessId == dwProcessId)
147 {
148 /* Make sure the thread ID matches too */
149 if (ThreadData->ThreadId == dwThreadId)
150 {
151 /* Check if we have a thread handle */
152 if (ThreadData->ThreadHandle)
153 {
154 /* Close it */
155 CloseHandle(ThreadData->ThreadHandle);
156 }
157
158 /* Check if we have a process handle */
159 if (ThreadData->ProcessHandle)
160 {
161 /* Close it */
162 CloseHandle(ThreadData->ProcessHandle);
163 }
164
165 /* Unlink the thread data */
166 DbgSsSetThreadData(ThreadData->Next);
167
168 /* Free it*/
169 RtlFreeHeap(RtlGetProcessHeap(), 0, ThreadData);
170
171 /* Move to the next structure */
172 ThreadData = DbgSsGetThreadData();
173 continue;
174 }
175 }
176
177 /* Move to the next one */
178 ThreadData = ThreadData->Next;
179 }
180 }
181
182 VOID
183 WINAPI
184 CloseAllProcessHandles(IN DWORD dwProcessId)
185 {
186 PDBGSS_THREAD_DATA ThreadData;
187
188 /* Loop all thread data events */
189 ThreadData = DbgSsGetThreadData();
190 while (ThreadData)
191 {
192 /* Check if this one matches */
193 if (ThreadData->ProcessId == dwProcessId)
194 {
195 /* Check if we have a thread handle */
196 if (ThreadData->ThreadHandle)
197 {
198 /* Close it */
199 CloseHandle(ThreadData->ThreadHandle);
200 }
201
202 /* Check if we have a process handle */
203 if (ThreadData->ProcessHandle)
204 {
205 /* Close it */
206 CloseHandle(ThreadData->ProcessHandle);
207 }
208
209 /* Unlink the thread data */
210 DbgSsSetThreadData(ThreadData->Next);
211
212 /* Free it*/
213 RtlFreeHeap(RtlGetProcessHeap(), 0, ThreadData);
214
215 /* Move to the next structure */
216 ThreadData = DbgSsGetThreadData();
217 continue;
218 }
219
220 /* Move to the next one */
221 ThreadData = ThreadData->Next;
222 }
223 }
224
225 HANDLE
226 WINAPI
227 ProcessIdToHandle(IN DWORD dwProcessId)
228 {
229 NTSTATUS Status;
230 OBJECT_ATTRIBUTES ObjectAttributes;
231 HANDLE Handle;
232 CLIENT_ID ClientId;
233
234 /* If we don't have a PID, look it up */
235 if (dwProcessId == -1U) dwProcessId = (DWORD)CsrGetProcessId();
236
237 /* Open a handle to the process */
238 ClientId.UniqueThread = NULL;
239 ClientId.UniqueProcess = (HANDLE)dwProcessId;
240 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
241 Status = NtOpenProcess(&Handle,
242 PROCESS_ALL_ACCESS,
243 &ObjectAttributes,
244 &ClientId);
245 if (!NT_SUCCESS(Status))
246 {
247 /* Fail */
248 SetLastErrorByStatus(Status);
249 return 0;
250 }
251
252 /* Return the handle */
253 return Handle;
254 }
255
256 /* PUBLIC FUNCTIONS **********************************************************/
257
258 /*
259 * @implemented
260 */
261 BOOL
262 WINAPI
263 CheckRemoteDebuggerPresent(IN HANDLE hProcess,
264 OUT PBOOL pbDebuggerPresent)
265 {
266 HANDLE DebugPort;
267 NTSTATUS Status;
268
269 /* Make sure we have an output and process*/
270 if (!(pbDebuggerPresent) || !(hProcess))
271 {
272 /* Fail */
273 SetLastError(ERROR_INVALID_PARAMETER);
274 return FALSE;
275 }
276
277 /* Check if the process has a debug object/port */
278 Status = NtQueryInformationProcess(hProcess,
279 ProcessDebugPort,
280 (PVOID)&DebugPort,
281 sizeof(HANDLE),
282 NULL);
283 if (NT_SUCCESS(Status))
284 {
285 /* Return the current state */
286 *pbDebuggerPresent = (DebugPort) ? TRUE : FALSE;
287 return TRUE;
288 }
289
290 /* Otherwise, fail */
291 SetLastErrorByStatus(Status);
292 return FALSE;
293 }
294
295 /*
296 * @implemented
297 */
298 BOOL
299 WINAPI
300 ContinueDebugEvent(IN DWORD dwProcessId,
301 IN DWORD dwThreadId,
302 IN DWORD dwContinueStatus)
303 {
304 CLIENT_ID ClientId;
305 NTSTATUS Status;
306
307 /* Set the Client ID */
308 ClientId.UniqueProcess = (HANDLE)dwProcessId;
309 ClientId.UniqueThread = (HANDLE)dwThreadId;
310
311 /* Continue debugging */
312 Status = DbgUiContinue(&ClientId, dwContinueStatus);
313 if (!NT_SUCCESS(Status))
314 {
315 /* Fail */
316 SetLastErrorByStatus(Status);
317 return FALSE;
318 }
319
320 /* Remove the process/thread handles */
321 RemoveHandles(dwProcessId, dwThreadId);
322
323 /* Success */
324 return TRUE;
325 }
326
327 /*
328 * @implemented
329 */
330 BOOL
331 WINAPI
332 DebugActiveProcess(IN DWORD dwProcessId)
333 {
334 NTSTATUS Status;
335 HANDLE Handle;
336
337 /* Connect to the debugger */
338 Status = DbgUiConnectToDbg();
339 if (!NT_SUCCESS(Status))
340 {
341 SetLastErrorByStatus(Status);
342 return FALSE;
343 }
344
345 /* Get the process handle */
346 Handle = ProcessIdToHandle(dwProcessId);
347 if (!Handle) return FALSE;
348
349 /* Now debug the process */
350 Status = DbgUiDebugActiveProcess(Handle);
351 NtClose(Handle);
352
353 /* Check if debugging worked */
354 if (!NT_SUCCESS(Status))
355 {
356 /* Fail */
357 SetLastErrorByStatus(Status);
358 return FALSE;
359 }
360
361 /* Success */
362 return TRUE;
363 }
364
365 /*
366 * @implemented
367 */
368 BOOL
369 WINAPI
370 DebugActiveProcessStop(IN DWORD dwProcessId)
371 {
372 NTSTATUS Status;
373 HANDLE Handle;
374
375 /* Get the process handle */
376 Handle = ProcessIdToHandle(dwProcessId);
377 if (!Handle) return FALSE;
378
379 /* Close all the process handles */
380 CloseAllProcessHandles(dwProcessId);
381
382 /* Now stop debgging the process */
383 Status = DbgUiStopDebugging(Handle);
384 NtClose(Handle);
385
386 /* Check for failure */
387 if (!NT_SUCCESS(Status))
388 {
389 /* Fail */
390 SetLastError(ERROR_ACCESS_DENIED);
391 return FALSE;
392 }
393
394 /* Success */
395 return TRUE;
396 }
397
398 /*
399 * @implemented
400 */
401 BOOL
402 WINAPI
403 DebugBreakProcess(IN HANDLE Process)
404 {
405 NTSTATUS Status;
406
407 /* Send the breakin request */
408 Status = DbgUiIssueRemoteBreakin(Process);
409 if(!NT_SUCCESS(Status))
410 {
411 /* Failure */
412 SetLastErrorByStatus(Status);
413 return FALSE;
414 }
415
416 /* Success */
417 return TRUE;
418 }
419
420 /*
421 * @implemented
422 */
423 BOOL
424 WINAPI
425 DebugSetProcessKillOnExit(IN BOOL KillOnExit)
426 {
427 HANDLE Handle;
428 NTSTATUS Status;
429 ULONG State;
430
431 /* Get the debug object */
432 Handle = DbgUiGetThreadDebugObject();
433 if (!Handle)
434 {
435 /* Fail */
436 SetLastErrorByStatus(STATUS_INVALID_HANDLE);
437 return FALSE;
438 }
439
440 /* Now set the kill-on-exit state */
441 State = KillOnExit;
442 Status = NtSetInformationDebugObject(Handle,
443 DebugObjectKillProcessOnExitInformation,
444 &State,
445 sizeof(State),
446 NULL);
447 if (!NT_SUCCESS(Status))
448 {
449 /* Fail */
450 SetLastErrorByStatus(Status);
451 return FALSE;
452 }
453
454 /* Success */
455 return TRUE;
456 }
457
458 /*
459 * @implemented
460 */
461 BOOL
462 WINAPI
463 IsDebuggerPresent(VOID)
464 {
465 return (BOOL)NtCurrentPeb()->BeingDebugged;
466 }
467
468 /*
469 * @implemented
470 */
471 BOOL
472 WINAPI
473 WaitForDebugEvent(IN LPDEBUG_EVENT lpDebugEvent,
474 IN DWORD dwMilliseconds)
475 {
476 LARGE_INTEGER WaitTime;
477 PLARGE_INTEGER Timeout;
478 DBGUI_WAIT_STATE_CHANGE WaitStateChange;
479 NTSTATUS Status;
480
481 /* Check if this is an infinite wait */
482 if (dwMilliseconds == INFINITE)
483 {
484 /* Under NT, this means no timer argument */
485 Timeout = NULL;
486 }
487 else
488 {
489 /* Otherwise, convert the time to NT Format */
490 WaitTime.QuadPart = UInt32x32To64(-10000, dwMilliseconds);
491 Timeout = &WaitTime;
492 }
493
494 /* Loop while we keep getting interrupted */
495 do
496 {
497 /* Call the native API */
498 Status = DbgUiWaitStateChange(&WaitStateChange, Timeout);
499 } while ((Status == STATUS_ALERTED) || (Status == STATUS_USER_APC));
500
501 /* Check if the wait failed */
502 if (!(NT_SUCCESS(Status)) || (Status == DBG_UNABLE_TO_PROVIDE_HANDLE))
503 {
504 /* Set the error code and quit */
505 SetLastErrorByStatus(Status);
506 return FALSE;
507 }
508
509 /* Check if we timed out */
510 if (Status == STATUS_TIMEOUT)
511 {
512 /* Fail with a timeout error */
513 SetLastError(ERROR_SEM_TIMEOUT);
514 return FALSE;
515 }
516
517 /* Convert the structure */
518 Status = DbgUiConvertStateChangeStructure(&WaitStateChange, lpDebugEvent);
519 if (!NT_SUCCESS(Status))
520 {
521 /* Set the error code and quit */
522 SetLastErrorByStatus(Status);
523 return FALSE;
524 }
525
526 /* Check what kind of event this was */
527 switch (lpDebugEvent->dwDebugEventCode)
528 {
529 /* New thread was created */
530 case CREATE_THREAD_DEBUG_EVENT:
531
532 /* Setup the thread data */
533 SaveThreadHandle(lpDebugEvent->dwProcessId,
534 lpDebugEvent->dwThreadId,
535 lpDebugEvent->u.CreateThread.hThread);
536 break;
537
538 /* New process was created */
539 case CREATE_PROCESS_DEBUG_EVENT:
540
541 /* Setup the process data */
542 SaveProcessHandle(lpDebugEvent->dwProcessId,
543 lpDebugEvent->u.CreateProcessInfo.hProcess);
544
545 /* Setup the thread data */
546 SaveThreadHandle(lpDebugEvent->dwProcessId,
547 lpDebugEvent->dwThreadId,
548 lpDebugEvent->u.CreateThread.hThread);
549 break;
550
551 /* Process was exited */
552 case EXIT_PROCESS_DEBUG_EVENT:
553
554 /* Mark the thread data as such */
555 MarkProcessHandle(lpDebugEvent->dwProcessId);
556 break;
557
558 /* Thread was exited */
559 case EXIT_THREAD_DEBUG_EVENT:
560
561 /* Mark the thread data */
562 MarkThreadHandle(lpDebugEvent->dwThreadId);
563 break;
564
565 /* Nothing to do for anything else */
566 default:
567 break;
568 }
569
570 /* Return success */
571 return TRUE;
572 }
573
574 /* EOF */