- Update to r53061
[reactos.git] / dll / win32 / kernel32 / client / 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 static
34 HANDLE
35 K32CreateDBMonMutex(void)
36 {
37 static SID_IDENTIFIER_AUTHORITY siaNTAuth = {SECURITY_NT_AUTHORITY};
38 static SID_IDENTIFIER_AUTHORITY siaWorldAuth = {SECURITY_WORLD_SID_AUTHORITY};
39 HANDLE hMutex;
40
41 /* SIDs to be used in the DACL */
42 PSID psidSystem = NULL;
43 PSID psidAdministrators = NULL;
44 PSID psidEveryone = NULL;
45
46 /* buffer for the DACL */
47 PVOID pDaclBuf = NULL;
48
49 /* minimum size of the DACL: an ACL descriptor and three ACCESS_ALLOWED_ACE
50 headers. We'll add the size of SIDs when we'll know it
51 */
52 SIZE_T nDaclBufSize =
53 sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE) -
54 sizeof(((ACCESS_ALLOWED_ACE*)0)->SidStart)) * 3;
55
56 /* security descriptor of the mutex */
57 SECURITY_DESCRIPTOR sdMutexSecurity;
58
59 /* attributes of the mutex object we'll create */
60 SECURITY_ATTRIBUTES saMutexAttribs = {sizeof(saMutexAttribs),
61 &sdMutexSecurity,
62 TRUE};
63
64 NTSTATUS nErrCode;
65
66 /* first, try to open the mutex */
67 hMutex = OpenMutexW (SYNCHRONIZE | READ_CONTROL | MUTANT_QUERY_STATE,
68 TRUE,
69 L"DBWinMutex");
70
71 if(hMutex != NULL)
72 {
73 /* success */
74 return hMutex;
75 }
76 /* error other than the mutex not being found */
77 else if(GetLastError() != ERROR_FILE_NOT_FOUND)
78 {
79 /* failure */
80 return NULL;
81 }
82
83 /* if the mutex doesn't exist, create it */
84
85 /* first, set up the mutex security */
86 /* allocate the NT AUTHORITY\SYSTEM SID */
87 nErrCode = RtlAllocateAndInitializeSid(&siaNTAuth,
88 1,
89 SECURITY_LOCAL_SYSTEM_RID,
90 0,
91 0,
92 0,
93 0,
94 0,
95 0,
96 0,
97 &psidSystem);
98
99 /* failure */
100 if(!NT_SUCCESS(nErrCode)) goto l_Cleanup;
101
102 /* allocate the BUILTIN\Administrators SID */
103 nErrCode = RtlAllocateAndInitializeSid(&siaNTAuth,
104 2,
105 SECURITY_BUILTIN_DOMAIN_RID,
106 DOMAIN_ALIAS_RID_ADMINS,
107 0,
108 0,
109 0,
110 0,
111 0,
112 0,
113 &psidAdministrators);
114
115 /* failure */
116 if(!NT_SUCCESS(nErrCode)) goto l_Cleanup;
117
118 /* allocate the Everyone SID */
119 nErrCode = RtlAllocateAndInitializeSid(&siaWorldAuth,
120 1,
121 0,
122 0,
123 0,
124 0,
125 0,
126 0,
127 0,
128 0,
129 &psidEveryone);
130
131 /* failure */
132 if(!NT_SUCCESS(nErrCode)) goto l_Cleanup;
133
134 /* allocate space for the SIDs too */
135 nDaclBufSize += RtlLengthSid(psidSystem);
136 nDaclBufSize += RtlLengthSid(psidAdministrators);
137 nDaclBufSize += RtlLengthSid(psidEveryone);
138
139 /* allocate the buffer for the DACL */
140 pDaclBuf = GlobalAlloc(GMEM_FIXED, nDaclBufSize);
141
142 /* failure */
143 if(pDaclBuf == NULL) goto l_Cleanup;
144
145 /* create the DACL */
146 nErrCode = RtlCreateAcl(pDaclBuf, nDaclBufSize, ACL_REVISION);
147
148 /* failure */
149 if(!NT_SUCCESS(nErrCode)) goto l_Cleanup;
150
151 /* grant the minimum required access to Everyone */
152 nErrCode = RtlAddAccessAllowedAce(pDaclBuf,
153 ACL_REVISION,
154 SYNCHRONIZE |
155 READ_CONTROL |
156 MUTANT_QUERY_STATE,
157 psidEveryone);
158
159 /* failure */
160 if(!NT_SUCCESS(nErrCode)) goto l_Cleanup;
161
162 /* grant full access to BUILTIN\Administrators */
163 nErrCode = RtlAddAccessAllowedAce(pDaclBuf,
164 ACL_REVISION,
165 MUTANT_ALL_ACCESS,
166 psidAdministrators);
167
168 /* failure */
169 if(!NT_SUCCESS(nErrCode)) goto l_Cleanup;
170
171 /* grant full access to NT AUTHORITY\SYSTEM */
172 nErrCode = RtlAddAccessAllowedAce(pDaclBuf,
173 ACL_REVISION,
174 MUTANT_ALL_ACCESS,
175 psidSystem);
176
177 /* failure */
178 if(!NT_SUCCESS(nErrCode)) goto l_Cleanup;
179
180 /* create the security descriptor */
181 nErrCode = RtlCreateSecurityDescriptor(&sdMutexSecurity,
182 SECURITY_DESCRIPTOR_REVISION);
183
184 /* failure */
185 if(!NT_SUCCESS(nErrCode)) goto l_Cleanup;
186
187 /* set the descriptor's DACL to the ACL we created */
188 nErrCode = RtlSetDaclSecurityDescriptor(&sdMutexSecurity,
189 TRUE,
190 pDaclBuf,
191 FALSE);
192
193 /* failure */
194 if(!NT_SUCCESS(nErrCode)) goto l_Cleanup;
195
196 /* create the mutex */
197 hMutex = CreateMutexW(&saMutexAttribs, FALSE, L"DBWinMutex");
198
199 l_Cleanup:
200 /* free the buffers */
201 if(pDaclBuf) GlobalFree(pDaclBuf);
202 if(psidEveryone) RtlFreeSid(psidEveryone);
203 if(psidAdministrators) RtlFreeSid(psidAdministrators);
204 if(psidSystem) RtlFreeSid(psidSystem);
205
206 return hMutex;
207 }
208
209 VOID
210 WINAPI
211 SaveThreadHandle(IN DWORD dwProcessId,
212 IN DWORD dwThreadId,
213 IN HANDLE hThread)
214 {
215 PDBGSS_THREAD_DATA ThreadData;
216
217 /* Allocate a thread structure */
218 ThreadData = RtlAllocateHeap(RtlGetProcessHeap(),
219 0,
220 sizeof(DBGSS_THREAD_DATA));
221 if (!ThreadData) return;
222
223 /* Fill it out */
224 ThreadData->ThreadHandle = hThread;
225 ThreadData->ProcessId = dwProcessId;
226 ThreadData->ThreadId = dwThreadId;
227 ThreadData->ProcessHandle = NULL;
228 ThreadData->HandleMarked = FALSE;
229
230 /* Link it */
231 ThreadData->Next = DbgSsGetThreadData();
232 DbgSsSetThreadData(ThreadData);
233 }
234
235 VOID
236 WINAPI
237 SaveProcessHandle(IN DWORD dwProcessId,
238 IN HANDLE hProcess)
239 {
240 PDBGSS_THREAD_DATA ThreadData;
241
242 /* Allocate a thread structure */
243 ThreadData = RtlAllocateHeap(RtlGetProcessHeap(),
244 0,
245 sizeof(DBGSS_THREAD_DATA));
246 if (!ThreadData) return;
247
248 /* Fill it out */
249 ThreadData->ProcessHandle = hProcess;
250 ThreadData->ProcessId = dwProcessId;
251 ThreadData->ThreadId = 0;
252 ThreadData->ThreadHandle = NULL;
253 ThreadData->HandleMarked = FALSE;
254
255 /* Link it */
256 ThreadData->Next = DbgSsGetThreadData();
257 DbgSsSetThreadData(ThreadData);
258 }
259
260 VOID
261 WINAPI
262 MarkThreadHandle(IN DWORD dwThreadId)
263 {
264 PDBGSS_THREAD_DATA ThreadData;
265
266 /* Loop all thread data events */
267 for (ThreadData = DbgSsGetThreadData(); ThreadData; ThreadData = ThreadData->Next)
268 {
269 /* Check if this one matches */
270 if (ThreadData->ThreadId == dwThreadId)
271 {
272 /* Mark the structure and break out */
273 ThreadData->HandleMarked = TRUE;
274 break;
275 }
276 }
277 }
278
279 VOID
280 WINAPI
281 MarkProcessHandle(IN DWORD dwProcessId)
282 {
283 PDBGSS_THREAD_DATA ThreadData;
284
285 /* Loop all thread data events */
286 for (ThreadData = DbgSsGetThreadData(); ThreadData; ThreadData = ThreadData->Next)
287 {
288 /* Check if this one matches */
289 if ((ThreadData->ProcessId == dwProcessId) && !(ThreadData->ThreadId))
290 {
291 /* Mark the structure and break out */
292 ThreadData->HandleMarked = TRUE;
293 break;
294 }
295 }
296 }
297
298 VOID
299 WINAPI
300 RemoveHandles(IN DWORD dwProcessId,
301 IN DWORD dwThreadId)
302 {
303 PDBGSS_THREAD_DATA *ThreadData;
304 PDBGSS_THREAD_DATA ThisData;
305
306 /* Loop all thread data events */
307 ThreadData = (PDBGSS_THREAD_DATA*)NtCurrentTeb()->DbgSsReserved;
308 for (ThisData = *ThreadData; ThisData; ThisData = ThisData->Next)
309 {
310 /* Check if this one matches */
311 if ((ThisData->HandleMarked) &&
312 ((ThisData->ProcessId == dwProcessId) || (ThisData->ThreadId == dwThreadId)))
313 {
314 /* Close open handles */
315 if (ThisData->ThreadHandle) CloseHandle(ThisData->ThreadHandle);
316 if (ThisData->ProcessHandle) CloseHandle(ThisData->ProcessHandle);
317
318 /* Unlink the thread data */
319 *ThreadData = ThisData->Next;
320
321 /* Free it*/
322 RtlFreeHeap(RtlGetProcessHeap(), 0, ThisData);
323 }
324 else
325 {
326 /* Move to the next one */
327 ThreadData = &ThisData->Next;
328 }
329 }
330 }
331
332 VOID
333 WINAPI
334 CloseAllProcessHandles(IN DWORD dwProcessId)
335 {
336 PDBGSS_THREAD_DATA *ThreadData;
337 PDBGSS_THREAD_DATA ThisData;
338
339 /* Loop all thread data events */
340 ThreadData = (PDBGSS_THREAD_DATA*)NtCurrentTeb()->DbgSsReserved;
341 for (ThisData = *ThreadData; ThisData; ThisData = ThisData->Next)
342 {
343 /* Check if this one matches */
344 if (ThisData->ProcessId == dwProcessId)
345 {
346 /* Close open handles */
347 if (ThisData->ThreadHandle) CloseHandle(ThisData->ThreadHandle);
348 if (ThisData->ProcessHandle) CloseHandle(ThisData->ProcessHandle);
349
350 /* Unlink the thread data */
351 *ThreadData = ThisData->Next;
352
353 /* Free it*/
354 RtlFreeHeap(RtlGetProcessHeap(), 0, ThisData);
355 }
356 else
357 {
358 /* Move to the next one */
359 ThreadData = &ThisData->Next;
360 }
361 }
362 }
363
364 HANDLE
365 WINAPI
366 ProcessIdToHandle(IN DWORD dwProcessId)
367 {
368 NTSTATUS Status;
369 OBJECT_ATTRIBUTES ObjectAttributes;
370 HANDLE Handle;
371 CLIENT_ID ClientId;
372
373 /* If we don't have a PID, look it up */
374 if (dwProcessId == MAXDWORD) dwProcessId = (DWORD_PTR)CsrGetProcessId();
375
376 /* Open a handle to the process */
377 ClientId.UniqueThread = NULL;
378 ClientId.UniqueProcess = UlongToHandle(dwProcessId);
379 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
380 Status = NtOpenProcess(&Handle,
381 PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |
382 PROCESS_VM_WRITE | PROCESS_VM_READ |
383 PROCESS_SUSPEND_RESUME | PROCESS_QUERY_INFORMATION,
384 &ObjectAttributes,
385 &ClientId);
386 if (!NT_SUCCESS(Status))
387 {
388 /* Fail */
389 BaseSetLastNTError(Status);
390 return 0;
391 }
392
393 /* Return the handle */
394 return Handle;
395 }
396
397 /* PUBLIC FUNCTIONS **********************************************************/
398
399 /*
400 * @implemented
401 */
402 BOOL
403 WINAPI
404 CheckRemoteDebuggerPresent(IN HANDLE hProcess,
405 OUT PBOOL pbDebuggerPresent)
406 {
407 HANDLE DebugPort;
408 NTSTATUS Status;
409
410 /* Make sure we have an output and process*/
411 if (!(pbDebuggerPresent) || !(hProcess))
412 {
413 /* Fail */
414 SetLastError(ERROR_INVALID_PARAMETER);
415 return FALSE;
416 }
417
418 /* Check if the process has a debug object/port */
419 Status = NtQueryInformationProcess(hProcess,
420 ProcessDebugPort,
421 (PVOID)&DebugPort,
422 sizeof(HANDLE),
423 NULL);
424 if (NT_SUCCESS(Status))
425 {
426 /* Return the current state */
427 *pbDebuggerPresent = DebugPort != NULL;
428 return TRUE;
429 }
430
431 /* Otherwise, fail */
432 BaseSetLastNTError(Status);
433 return FALSE;
434 }
435
436 /*
437 * @implemented
438 */
439 BOOL
440 WINAPI
441 ContinueDebugEvent(IN DWORD dwProcessId,
442 IN DWORD dwThreadId,
443 IN DWORD dwContinueStatus)
444 {
445 CLIENT_ID ClientId;
446 NTSTATUS Status;
447
448 /* Set the Client ID */
449 ClientId.UniqueProcess = (HANDLE)dwProcessId;
450 ClientId.UniqueThread = (HANDLE)dwThreadId;
451
452 /* Continue debugging */
453 Status = DbgUiContinue(&ClientId, dwContinueStatus);
454 if (!NT_SUCCESS(Status))
455 {
456 /* Fail */
457 BaseSetLastNTError(Status);
458 return FALSE;
459 }
460
461 /* Remove the process/thread handles */
462 RemoveHandles(dwProcessId, dwThreadId);
463
464 /* Success */
465 return TRUE;
466 }
467
468 /*
469 * @implemented
470 */
471 BOOL
472 WINAPI
473 DebugActiveProcess(IN DWORD dwProcessId)
474 {
475 NTSTATUS Status, Status1;
476 HANDLE Handle;
477
478 /* Connect to the debugger */
479 Status = DbgUiConnectToDbg();
480 if (!NT_SUCCESS(Status))
481 {
482 BaseSetLastNTError(Status);
483 return FALSE;
484 }
485
486 /* Get the process handle */
487 Handle = ProcessIdToHandle(dwProcessId);
488 if (!Handle) return FALSE;
489
490 /* Now debug the process */
491 Status = DbgUiDebugActiveProcess(Handle);
492
493 /* Close the handle since we're done */
494 Status1 = NtClose(Handle);
495 ASSERT(NT_SUCCESS(Status1));
496
497 /* Check if debugging worked */
498 if (!NT_SUCCESS(Status))
499 {
500 /* Fail */
501 BaseSetLastNTError(Status);
502 return FALSE;
503 }
504
505 /* Success */
506 return TRUE;
507 }
508
509 /*
510 * @implemented
511 */
512 BOOL
513 WINAPI
514 DebugActiveProcessStop(IN DWORD dwProcessId)
515 {
516 NTSTATUS Status, Status1;
517 HANDLE Handle;
518
519 /* Get the process handle */
520 Handle = ProcessIdToHandle(dwProcessId);
521 if (!Handle) return FALSE;
522
523 /* Close all the process handles */
524 CloseAllProcessHandles(dwProcessId);
525
526 /* Now stop debgging the process */
527 Status = DbgUiStopDebugging(Handle);
528 Status1 = NtClose(Handle);
529 ASSERT(NT_SUCCESS(Status1));
530
531 /* Check for failure */
532 if (!NT_SUCCESS(Status))
533 {
534 /* Fail */
535 SetLastError(ERROR_ACCESS_DENIED);
536 return FALSE;
537 }
538
539 /* Success */
540 return TRUE;
541 }
542
543 /*
544 * @implemented
545 */
546 BOOL
547 WINAPI
548 DebugBreakProcess(IN HANDLE Process)
549 {
550 NTSTATUS Status;
551
552 /* Send the breakin request */
553 Status = DbgUiIssueRemoteBreakin(Process);
554 if(!NT_SUCCESS(Status))
555 {
556 /* Failure */
557 BaseSetLastNTError(Status);
558 return FALSE;
559 }
560
561 /* Success */
562 return TRUE;
563 }
564
565 /*
566 * @implemented
567 */
568 BOOL
569 WINAPI
570 DebugSetProcessKillOnExit(IN BOOL KillOnExit)
571 {
572 HANDLE Handle;
573 NTSTATUS Status;
574 ULONG State;
575
576 /* Get the debug object */
577 Handle = DbgUiGetThreadDebugObject();
578 if (!Handle)
579 {
580 /* Fail */
581 BaseSetLastNTError(STATUS_INVALID_HANDLE);
582 return FALSE;
583 }
584
585 /* Now set the kill-on-exit state */
586 State = KillOnExit != 0;
587 Status = NtSetInformationDebugObject(Handle,
588 DebugObjectKillProcessOnExitInformation,
589 &State,
590 sizeof(State),
591 NULL);
592 if (!NT_SUCCESS(Status))
593 {
594 /* Fail */
595 BaseSetLastNTError(Status);
596 return FALSE;
597 }
598
599 /* Success */
600 return TRUE;
601 }
602
603 /*
604 * @implemented
605 */
606 BOOL
607 WINAPI
608 IsDebuggerPresent(VOID)
609 {
610 return (BOOL)NtCurrentPeb()->BeingDebugged;
611 }
612
613 /*
614 * @implemented
615 */
616 BOOL
617 WINAPI
618 WaitForDebugEvent(IN LPDEBUG_EVENT lpDebugEvent,
619 IN DWORD dwMilliseconds)
620 {
621 LARGE_INTEGER WaitTime;
622 PLARGE_INTEGER Timeout;
623 DBGUI_WAIT_STATE_CHANGE WaitStateChange;
624 NTSTATUS Status;
625
626 /* Convert to NT Timeout */
627 Timeout = BaseFormatTimeOut(&WaitTime, dwMilliseconds);
628
629 /* Loop while we keep getting interrupted */
630 do
631 {
632 /* Call the native API */
633 Status = DbgUiWaitStateChange(&WaitStateChange, Timeout);
634 } while ((Status == STATUS_ALERTED) || (Status == STATUS_USER_APC));
635
636 /* Check if the wait failed */
637 if (!(NT_SUCCESS(Status)) || (Status == DBG_UNABLE_TO_PROVIDE_HANDLE))
638 {
639 /* Set the error code and quit */
640 BaseSetLastNTError(Status);
641 return FALSE;
642 }
643
644 /* Check if we timed out */
645 if (Status == STATUS_TIMEOUT)
646 {
647 /* Fail with a timeout error */
648 SetLastError(ERROR_SEM_TIMEOUT);
649 return FALSE;
650 }
651
652 /* Convert the structure */
653 Status = DbgUiConvertStateChangeStructure(&WaitStateChange, lpDebugEvent);
654 if (!NT_SUCCESS(Status))
655 {
656 /* Set the error code and quit */
657 BaseSetLastNTError(Status);
658 return FALSE;
659 }
660
661 /* Check what kind of event this was */
662 switch (lpDebugEvent->dwDebugEventCode)
663 {
664 /* New thread was created */
665 case CREATE_THREAD_DEBUG_EVENT:
666
667 /* Setup the thread data */
668 SaveThreadHandle(lpDebugEvent->dwProcessId,
669 lpDebugEvent->dwThreadId,
670 lpDebugEvent->u.CreateThread.hThread);
671 break;
672
673 /* New process was created */
674 case CREATE_PROCESS_DEBUG_EVENT:
675
676 /* Setup the process data */
677 SaveProcessHandle(lpDebugEvent->dwProcessId,
678 lpDebugEvent->u.CreateProcessInfo.hProcess);
679
680 /* Setup the thread data */
681 SaveThreadHandle(lpDebugEvent->dwProcessId,
682 lpDebugEvent->dwThreadId,
683 lpDebugEvent->u.CreateProcessInfo.hThread);
684 break;
685
686 /* Process was exited */
687 case EXIT_PROCESS_DEBUG_EVENT:
688
689 /* Mark the thread data as such and fall through */
690 MarkProcessHandle(lpDebugEvent->dwProcessId);
691
692 /* Thread was exited */
693 case EXIT_THREAD_DEBUG_EVENT:
694
695 /* Mark the thread data */
696 MarkThreadHandle(lpDebugEvent->dwThreadId);
697 break;
698
699 /* Nothing to do */
700 case EXCEPTION_DEBUG_EVENT:
701 case LOAD_DLL_DEBUG_EVENT:
702 case UNLOAD_DLL_DEBUG_EVENT:
703 case OUTPUT_DEBUG_STRING_EVENT:
704 case RIP_EVENT:
705 break;
706
707 /* Fail anything else */
708 default:
709 return FALSE;
710 }
711
712 /* Return success */
713 return TRUE;
714 }
715
716 /*
717 * @implemented
718 */
719 VOID
720 WINAPI
721 OutputDebugStringA(IN LPCSTR _OutputString)
722 {
723 _SEH2_TRY
724 {
725 ULONG_PTR a_nArgs[2];
726
727 a_nArgs[0] = (ULONG_PTR)(strlen(_OutputString) + 1);
728 a_nArgs[1] = (ULONG_PTR)_OutputString;
729
730 /* send the string to the user-mode debugger */
731 RaiseException(DBG_PRINTEXCEPTION_C, 0, 2, a_nArgs);
732 }
733 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
734 {
735 /* no user-mode debugger: try the systemwide debug message monitor, or the
736 kernel debugger as a last resort */
737
738 /* mutex used to synchronize invocations of OutputDebugString */
739 static HANDLE s_hDBMonMutex = NULL;
740 /* true if we already attempted to open/create the mutex */
741 static BOOL s_bDBMonMutexTriedOpen = FALSE;
742
743 /* local copy of the mutex handle */
744 volatile HANDLE hDBMonMutex = s_hDBMonMutex;
745 /* handle to the Section of the shared buffer */
746 volatile HANDLE hDBMonBuffer = NULL;
747
748 /* pointer to the mapped view of the shared buffer. It consist of the current
749 process id followed by the message string */
750 struct { DWORD ProcessId; CHAR Buffer[1]; } * pDBMonBuffer = NULL;
751
752 /* event: signaled by the debug message monitor when OutputDebugString can write
753 to the shared buffer */
754 volatile HANDLE hDBMonBufferReady = NULL;
755
756 /* event: to be signaled by OutputDebugString when it's done writing to the
757 shared buffer */
758 volatile HANDLE hDBMonDataReady = NULL;
759
760 /* mutex not opened, and no previous attempts to open/create it */
761 if(hDBMonMutex == NULL && !s_bDBMonMutexTriedOpen)
762 {
763 /* open/create the mutex */
764 hDBMonMutex = K32CreateDBMonMutex();
765 /* store the handle */
766 s_hDBMonMutex = hDBMonMutex;
767 }
768
769 _SEH2_TRY
770 {
771 volatile PCHAR a_cBuffer = NULL;
772
773 /* opening the mutex failed */
774 if(hDBMonMutex == NULL)
775 {
776 /* remember next time */
777 s_bDBMonMutexTriedOpen = TRUE;
778 }
779 /* opening the mutex succeeded */
780 else
781 {
782 do
783 {
784 /* synchronize with other invocations of OutputDebugString */
785 WaitForSingleObject(hDBMonMutex, INFINITE);
786
787 /* buffer of the system-wide debug message monitor */
788 hDBMonBuffer = OpenFileMappingW(SECTION_MAP_WRITE, FALSE, L"DBWIN_BUFFER");
789
790 /* couldn't open the buffer: send the string to the kernel debugger */
791 if(hDBMonBuffer == NULL) break;
792
793 /* map the buffer */
794 pDBMonBuffer = MapViewOfFile(hDBMonBuffer,
795 SECTION_MAP_READ | SECTION_MAP_WRITE,
796 0,
797 0,
798 0);
799
800 /* couldn't map the buffer: send the string to the kernel debugger */
801 if(pDBMonBuffer == NULL) break;
802
803 /* open the event signaling that the buffer can be accessed */
804 hDBMonBufferReady = OpenEventW(SYNCHRONIZE, FALSE, L"DBWIN_BUFFER_READY");
805
806 /* couldn't open the event: send the string to the kernel debugger */
807 if(hDBMonBufferReady == NULL) break;
808
809 /* open the event to be signaled when the buffer has been filled */
810 hDBMonDataReady = OpenEventW(EVENT_MODIFY_STATE, FALSE, L"DBWIN_DATA_READY");
811 }
812 while(0);
813
814 /* we couldn't connect to the system-wide debug message monitor: send the
815 string to the kernel debugger */
816 if(hDBMonDataReady == NULL) ReleaseMutex(hDBMonMutex);
817 }
818
819 _SEH2_TRY
820 {
821 /* size of the current output block */
822 volatile SIZE_T nRoundLen;
823
824 /* size of the remainder of the string */
825 volatile SIZE_T nOutputStringLen;
826
827 /* output the whole string */
828 nOutputStringLen = strlen(_OutputString);
829
830 do
831 {
832 /* we're connected to the debug monitor:
833 write the current block to the shared buffer */
834 if(hDBMonDataReady)
835 {
836 /* wait a maximum of 10 seconds for the debug monitor
837 to finish processing the shared buffer */
838 if(WaitForSingleObject(hDBMonBufferReady, 10000) != WAIT_OBJECT_0)
839 {
840 /* timeout or failure: give up */
841 break;
842 }
843
844 /* write the process id into the buffer */
845 pDBMonBuffer->ProcessId = GetCurrentProcessId();
846
847 /* write only as many bytes as they fit in the buffer */
848 if(nOutputStringLen > (PAGE_SIZE - sizeof(DWORD) - 1))
849 nRoundLen = PAGE_SIZE - sizeof(DWORD) - 1;
850 else
851 nRoundLen = nOutputStringLen;
852
853 /* copy the current block into the buffer */
854 memcpy(pDBMonBuffer->Buffer, _OutputString, nRoundLen);
855
856 /* null-terminate the current block */
857 pDBMonBuffer->Buffer[nRoundLen] = 0;
858
859 /* signal that the data contains meaningful data and can be read */
860 SetEvent(hDBMonDataReady);
861 }
862 /* else, send the current block to the kernel debugger */
863 else
864 {
865 /* output in blocks of 512 characters */
866 a_cBuffer = (CHAR*)HeapAlloc(GetProcessHeap(), 0, 512);
867
868 if (!a_cBuffer)
869 {
870 DbgPrint("OutputDebugStringA: Failed\n");
871 break;
872 }
873
874 /* write a maximum of 511 bytes */
875 if(nOutputStringLen > 510)
876 nRoundLen = 510;
877 else
878 nRoundLen = nOutputStringLen;
879
880 /* copy the current block */
881 memcpy(a_cBuffer, _OutputString, nRoundLen);
882
883 /* null-terminate the current block */
884 a_cBuffer[nRoundLen] = 0;
885
886 /* send the current block to the kernel debugger */
887 DbgPrint("%s", a_cBuffer);
888
889 if (a_cBuffer)
890 {
891 HeapFree(GetProcessHeap(), 0, a_cBuffer);
892 a_cBuffer = NULL;
893 }
894 }
895
896 /* move to the next block */
897 _OutputString += nRoundLen;
898 nOutputStringLen -= nRoundLen;
899 }
900 /* repeat until the string has been fully output */
901 while (nOutputStringLen > 0);
902 }
903 /* ignore access violations and let other exceptions fall through */
904 _SEH2_EXCEPT((_SEH2_GetExceptionCode() == STATUS_ACCESS_VIOLATION) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
905 {
906 if (a_cBuffer)
907 HeapFree(GetProcessHeap(), 0, a_cBuffer);
908
909 /* string copied verbatim from Microsoft's kernel32.dll */
910 DbgPrint("\nOutputDebugString faulted during output\n");
911 }
912 _SEH2_END;
913 }
914 _SEH2_FINALLY
915 {
916 /* close all the still open resources */
917 if(hDBMonBufferReady) CloseHandle(hDBMonBufferReady);
918 if(pDBMonBuffer) UnmapViewOfFile(pDBMonBuffer);
919 if(hDBMonBuffer) CloseHandle(hDBMonBuffer);
920 if(hDBMonDataReady) CloseHandle(hDBMonDataReady);
921
922 /* leave the critical section */
923 if(hDBMonDataReady != NULL)
924 ReleaseMutex(hDBMonMutex);
925 }
926 _SEH2_END;
927 }
928 _SEH2_END;
929 }
930
931 /*
932 * @implemented
933 */
934 VOID
935 WINAPI
936 OutputDebugStringW(IN LPCWSTR OutputString)
937 {
938 UNICODE_STRING UnicodeString;
939 ANSI_STRING AnsiString;
940 NTSTATUS Status;
941
942 /* convert the string in ANSI */
943 RtlInitUnicodeString(&UnicodeString, OutputString);
944 Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE);
945
946 /* OutputDebugStringW always prints something, even if conversion fails */
947 if (!NT_SUCCESS(Status)) AnsiString.Buffer = "";
948
949 /* Output the converted string */
950 OutputDebugStringA(AnsiString.Buffer);
951
952 /* free the converted string */
953 if (NT_SUCCESS(Status)) RtlFreeAnsiString(&AnsiString);
954 }
955
956 /* EOF */