- Implement WaitForDebugEvent. Calls out to DbgUiConvertWaitStateStructure which...
[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 == -1) dwProcessId = (DWORD)CsrGetProcessId();
236
237 /* Open a handle to the process */
238 ClientId.UniqueProcess = (HANDLE)dwProcessId;
239 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
240 Status = NtOpenProcess(&Handle,
241 PROCESS_ALL_ACCESS,
242 &ObjectAttributes,
243 &ClientId);
244 if (!NT_SUCCESS(Status))
245 {
246 /* Fail */
247 SetLastErrorByStatus(Status);
248 return 0;
249 }
250
251 /* Return the handle */
252 return Handle;
253 }
254
255 /* PUBLIC FUNCTIONS **********************************************************/
256
257 /*
258 * @implemented
259 */
260 BOOL
261 WINAPI
262 CheckRemoteDebuggerPresent(IN HANDLE hProcess,
263 OUT PBOOL pbDebuggerPresent)
264 {
265 HANDLE DebugPort;
266 NTSTATUS Status;
267
268 /* Make sure we have an output and process*/
269 if (!(pbDebuggerPresent) || !(hProcess))
270 {
271 /* Fail */
272 SetLastError(ERROR_INVALID_PARAMETER);
273 return FALSE;
274 }
275
276 /* Check if the process has a debug object/port */
277 Status = NtQueryInformationProcess(hProcess,
278 ProcessDebugPort,
279 (PVOID)&DebugPort,
280 sizeof(HANDLE),
281 NULL);
282 if (NT_SUCCESS(Status))
283 {
284 /* Return the current state */
285 *pbDebuggerPresent = (DebugPort) ? TRUE : FALSE;
286 return TRUE;
287 }
288
289 /* Otherwise, fail */
290 SetLastErrorByStatus(Status);
291 return FALSE;
292 }
293
294 /*
295 * @implemented
296 */
297 BOOL
298 WINAPI
299 ContinueDebugEvent(IN DWORD dwProcessId,
300 IN DWORD dwThreadId,
301 IN DWORD dwContinueStatus)
302 {
303 CLIENT_ID ClientId;
304 NTSTATUS Status;
305
306 /* Set the Client ID */
307 ClientId.UniqueProcess = (HANDLE)dwProcessId;
308 ClientId.UniqueThread = (HANDLE)dwThreadId;
309
310 /* Continue debugging */
311 Status = DbgUiContinue(&ClientId, dwContinueStatus);
312 if (!NT_SUCCESS(Status))
313 {
314 /* Fail */
315 SetLastErrorByStatus(Status);
316 return FALSE;
317 }
318
319 /* Remove the process/thread handles */
320 RemoveHandles(dwProcessId, dwThreadId);
321
322 /* Success */
323 return TRUE;
324 }
325
326 /*
327 * @implemented
328 */
329 BOOL
330 WINAPI
331 DebugActiveProcess(IN DWORD dwProcessId)
332 {
333 NTSTATUS Status;
334 HANDLE Handle;
335
336 /* Connect to the debugger */
337 Status = DbgUiConnectToDbg();
338 if (!NT_SUCCESS(Status))
339 {
340 SetLastErrorByStatus(Status);
341 return FALSE;
342 }
343
344 /* Get the process handle */
345 Handle = ProcessIdToHandle(dwProcessId);
346 if (!Handle) return FALSE;
347
348 /* Now debug the process */
349 Status = DbgUiDebugActiveProcess(Handle);
350 NtClose(Handle);
351
352 /* Check if debugging worked */
353 if (!NT_SUCCESS(Status))
354 {
355 /* Fail */
356 SetLastErrorByStatus(Status);
357 return FALSE;
358 }
359
360 /* Success */
361 return TRUE;
362 }
363
364 /*
365 * @implemented
366 */
367 BOOL
368 WINAPI
369 DebugActiveProcessStop(IN DWORD dwProcessId)
370 {
371 NTSTATUS Status;
372 HANDLE Handle;
373
374 /* Get the process handle */
375 Handle = ProcessIdToHandle(dwProcessId);
376 if (!Handle) return FALSE;
377
378 /* Close all the process handles */
379 CloseAllProcessHandles(dwProcessId);
380
381 /* Now stop debgging the process */
382 Status = DbgUiStopDebugging(Handle);
383 NtClose(Handle);
384
385 /* Check for failure */
386 if (!NT_SUCCESS(Status))
387 {
388 /* Fail */
389 SetLastError(ERROR_ACCESS_DENIED);
390 return FALSE;
391 }
392
393 /* Success */
394 return TRUE;
395 }
396
397 /*
398 * @implemented
399 */
400 BOOL
401 WINAPI
402 DebugBreakProcess(IN HANDLE Process)
403 {
404 NTSTATUS Status;
405
406 /* Send the breakin request */
407 Status = DbgUiIssueRemoteBreakin(Process);
408 if(!NT_SUCCESS(Status))
409 {
410 /* Failure */
411 SetLastErrorByStatus(Status);
412 return FALSE;
413 }
414
415 /* Success */
416 return TRUE;
417 }
418
419 /*
420 * @implemented
421 */
422 BOOL
423 WINAPI
424 DebugSetProcessKillOnExit(IN BOOL KillOnExit)
425 {
426 HANDLE Handle;
427 NTSTATUS Status;
428 ULONG State;
429
430 /* Get the debug object */
431 Handle = DbgUiGetThreadDebugObject();
432 if (!Handle)
433 {
434 /* Fail */
435 SetLastErrorByStatus(STATUS_INVALID_HANDLE);
436 return FALSE;
437 }
438
439 /* Now set the kill-on-exit state */
440 State = KillOnExit;
441 Status = NtSetInformationDebugObject(Handle,
442 DebugObjectKillProcessOnExitInformation,
443 &State,
444 sizeof(State),
445 NULL);
446 if (!NT_SUCCESS(Status))
447 {
448 /* Fail */
449 SetLastError(Status);
450 return FALSE;
451 }
452
453 /* Success */
454 return TRUE;
455 }
456
457 /*
458 * @implemented
459 */
460 BOOL
461 WINAPI
462 IsDebuggerPresent(VOID)
463 {
464 return (BOOL)NtCurrentPeb()->BeingDebugged;
465 }
466
467 /*
468 * @implemented
469 */
470 BOOL
471 WINAPI
472 WaitForDebugEvent(IN LPDEBUG_EVENT lpDebugEvent,
473 IN DWORD dwMilliseconds)
474 {
475 LARGE_INTEGER WaitTime;
476 PLARGE_INTEGER Timeout;
477 DBGUI_WAIT_STATE_CHANGE WaitStateChange;
478 NTSTATUS Status;
479
480 /* Check if this is an infinite wait */
481 if (dwMilliseconds == INFINITE)
482 {
483 /* Under NT, this means no timer argument */
484 Timeout = NULL;
485 }
486 else
487 {
488 /* Otherwise, convert the time to NT Format */
489 WaitTime.QuadPart = UInt32x32To64(-10000, dwMilliseconds);
490 Timeout = &WaitTime;
491 }
492
493 /* Loop while we keep getting interrupted */
494 do
495 {
496 /* Call the native API */
497 Status = DbgUiWaitStateChange(&WaitStateChange, Timeout);
498 } while ((Status == STATUS_ALERTED) || (Status == STATUS_USER_APC));
499
500 /* Check if the wait failed */
501 if (!(NT_SUCCESS(Status)) || (Status != DBG_UNABLE_TO_PROVIDE_HANDLE))
502 {
503 /* Set the error code and quit */
504 SetLastErrorByStatus(Status);
505 return FALSE;
506 }
507
508 /* Check if we timed out */
509 if (Status == STATUS_TIMEOUT)
510 {
511 /* Fail with a timeout error */
512 SetLastError(ERROR_SEM_TIMEOUT);
513 return FALSE;
514 }
515
516 /* Convert the structure */
517 Status = DbgUiConvertStateChangeStructure(&WaitStateChange, lpDebugEvent);
518 if (!NT_SUCCESS(Status))
519 {
520 /* Set the error code and quit */
521 SetLastErrorByStatus(Status);
522 return FALSE;
523 }
524
525 /* Check what kind of event this was */
526 switch (lpDebugEvent->dwDebugEventCode)
527 {
528 /* New thread was created */
529 case CREATE_THREAD_DEBUG_EVENT:
530
531 /* Setup the thread data */
532 SaveThreadHandle(lpDebugEvent->dwProcessId,
533 lpDebugEvent->dwThreadId,
534 lpDebugEvent->u.CreateThread.hThread);
535 break;
536
537 /* New process was created */
538 case CREATE_PROCESS_DEBUG_EVENT:
539
540 /* Setup the process data */
541 SaveProcessHandle(lpDebugEvent->dwProcessId,
542 lpDebugEvent->u.CreateProcessInfo.hProcess);
543
544 /* Setup the thread data */
545 SaveThreadHandle(lpDebugEvent->dwProcessId,
546 lpDebugEvent->dwThreadId,
547 lpDebugEvent->u.CreateThread.hThread);
548 break;
549
550 /* Process was exited */
551 case EXIT_PROCESS_DEBUG_EVENT:
552
553 /* Mark the thread data as such */
554 MarkProcessHandle(lpDebugEvent->dwProcessId);
555 break;
556
557 /* Thread was exited */
558 case EXIT_THREAD_DEBUG_EVENT:
559
560 /* Mark the thread data */
561 MarkThreadHandle(lpDebugEvent->dwThreadId);
562 break;
563
564 /* Nothing to do for anything else */
565 default:
566 break;
567 }
568
569 /* Return success */
570 return TRUE;
571 }
572
573 /* EOF */