[KERNEL32][CONSRV]
[reactos.git] / reactos / 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 ThisData = *ThreadData;
309 while(ThisData)
310 {
311 /* Check if this one matches */
312 if ((ThisData->HandleMarked) &&
313 ((ThisData->ProcessId == dwProcessId) || (ThisData->ThreadId == dwThreadId)))
314 {
315 /* Close open handles */
316 if (ThisData->ThreadHandle) CloseHandle(ThisData->ThreadHandle);
317 if (ThisData->ProcessHandle) CloseHandle(ThisData->ProcessHandle);
318
319 /* Unlink the thread data */
320 *ThreadData = ThisData->Next;
321
322 /* Free it*/
323 RtlFreeHeap(RtlGetProcessHeap(), 0, ThisData);
324 }
325 else
326 {
327 /* Move to the next one */
328 ThreadData = &ThisData->Next;
329 }
330 ThisData = *ThreadData;
331 }
332 }
333
334 VOID
335 WINAPI
336 CloseAllProcessHandles(IN DWORD dwProcessId)
337 {
338 PDBGSS_THREAD_DATA *ThreadData;
339 PDBGSS_THREAD_DATA ThisData;
340
341 /* Loop all thread data events */
342 ThreadData = (PDBGSS_THREAD_DATA*)NtCurrentTeb()->DbgSsReserved;
343 ThisData = *ThreadData;
344 while(ThisData)
345 {
346 /* Check if this one matches */
347 if (ThisData->ProcessId == dwProcessId)
348 {
349 /* Close open handles */
350 if (ThisData->ThreadHandle) CloseHandle(ThisData->ThreadHandle);
351 if (ThisData->ProcessHandle) CloseHandle(ThisData->ProcessHandle);
352
353 /* Unlink the thread data */
354 *ThreadData = ThisData->Next;
355
356 /* Free it*/
357 RtlFreeHeap(RtlGetProcessHeap(), 0, ThisData);
358 }
359 else
360 {
361 /* Move to the next one */
362 ThreadData = &ThisData->Next;
363 }
364 ThisData = *ThreadData;
365 }
366 }
367
368 HANDLE
369 WINAPI
370 ProcessIdToHandle(IN DWORD dwProcessId)
371 {
372 NTSTATUS Status;
373 OBJECT_ATTRIBUTES ObjectAttributes;
374 HANDLE Handle;
375 CLIENT_ID ClientId;
376
377 /* If we don't have a PID, look it up */
378 if (dwProcessId == MAXDWORD) dwProcessId = (DWORD_PTR)CsrGetProcessId();
379
380 /* Open a handle to the process */
381 ClientId.UniqueThread = NULL;
382 ClientId.UniqueProcess = UlongToHandle(dwProcessId);
383 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
384 Status = NtOpenProcess(&Handle,
385 PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |
386 PROCESS_VM_WRITE | PROCESS_VM_READ |
387 PROCESS_SUSPEND_RESUME | PROCESS_QUERY_INFORMATION,
388 &ObjectAttributes,
389 &ClientId);
390 if (!NT_SUCCESS(Status))
391 {
392 /* Fail */
393 BaseSetLastNTError(Status);
394 return 0;
395 }
396
397 /* Return the handle */
398 return Handle;
399 }
400
401 /* PUBLIC FUNCTIONS **********************************************************/
402
403 /*
404 * @implemented
405 */
406 BOOL
407 WINAPI
408 CheckRemoteDebuggerPresent(IN HANDLE hProcess,
409 OUT PBOOL pbDebuggerPresent)
410 {
411 HANDLE DebugPort;
412 NTSTATUS Status;
413
414 /* Make sure we have an output and process*/
415 if (!(pbDebuggerPresent) || !(hProcess))
416 {
417 /* Fail */
418 SetLastError(ERROR_INVALID_PARAMETER);
419 return FALSE;
420 }
421
422 /* Check if the process has a debug object/port */
423 Status = NtQueryInformationProcess(hProcess,
424 ProcessDebugPort,
425 (PVOID)&DebugPort,
426 sizeof(HANDLE),
427 NULL);
428 if (NT_SUCCESS(Status))
429 {
430 /* Return the current state */
431 *pbDebuggerPresent = DebugPort != NULL;
432 return TRUE;
433 }
434
435 /* Otherwise, fail */
436 BaseSetLastNTError(Status);
437 return FALSE;
438 }
439
440 /*
441 * @implemented
442 */
443 BOOL
444 WINAPI
445 ContinueDebugEvent(IN DWORD dwProcessId,
446 IN DWORD dwThreadId,
447 IN DWORD dwContinueStatus)
448 {
449 CLIENT_ID ClientId;
450 NTSTATUS Status;
451
452 /* Set the Client ID */
453 ClientId.UniqueProcess = (HANDLE)dwProcessId;
454 ClientId.UniqueThread = (HANDLE)dwThreadId;
455
456 /* Continue debugging */
457 Status = DbgUiContinue(&ClientId, dwContinueStatus);
458 if (!NT_SUCCESS(Status))
459 {
460 /* Fail */
461 BaseSetLastNTError(Status);
462 return FALSE;
463 }
464
465 /* Remove the process/thread handles */
466 RemoveHandles(dwProcessId, dwThreadId);
467
468 /* Success */
469 return TRUE;
470 }
471
472 /*
473 * @implemented
474 */
475 BOOL
476 WINAPI
477 DebugActiveProcess(IN DWORD dwProcessId)
478 {
479 NTSTATUS Status, Status1;
480 HANDLE Handle;
481
482 /* Connect to the debugger */
483 Status = DbgUiConnectToDbg();
484 if (!NT_SUCCESS(Status))
485 {
486 BaseSetLastNTError(Status);
487 return FALSE;
488 }
489
490 /* Get the process handle */
491 Handle = ProcessIdToHandle(dwProcessId);
492 if (!Handle) return FALSE;
493
494 /* Now debug the process */
495 Status = DbgUiDebugActiveProcess(Handle);
496
497 /* Close the handle since we're done */
498 Status1 = NtClose(Handle);
499 ASSERT(NT_SUCCESS(Status1));
500
501 /* Check if debugging worked */
502 if (!NT_SUCCESS(Status))
503 {
504 /* Fail */
505 BaseSetLastNTError(Status);
506 return FALSE;
507 }
508
509 /* Success */
510 return TRUE;
511 }
512
513 /*
514 * @implemented
515 */
516 BOOL
517 WINAPI
518 DebugActiveProcessStop(IN DWORD dwProcessId)
519 {
520 NTSTATUS Status, Status1;
521 HANDLE Handle;
522
523 /* Get the process handle */
524 Handle = ProcessIdToHandle(dwProcessId);
525 if (!Handle) return FALSE;
526
527 /* Close all the process handles */
528 CloseAllProcessHandles(dwProcessId);
529
530 /* Now stop debgging the process */
531 Status = DbgUiStopDebugging(Handle);
532 Status1 = NtClose(Handle);
533 ASSERT(NT_SUCCESS(Status1));
534
535 /* Check for failure */
536 if (!NT_SUCCESS(Status))
537 {
538 /* Fail */
539 SetLastError(ERROR_ACCESS_DENIED);
540 return FALSE;
541 }
542
543 /* Success */
544 return TRUE;
545 }
546
547 /*
548 * @implemented
549 */
550 BOOL
551 WINAPI
552 DebugBreakProcess(IN HANDLE Process)
553 {
554 NTSTATUS Status;
555
556 /* Send the breakin request */
557 Status = DbgUiIssueRemoteBreakin(Process);
558 if(!NT_SUCCESS(Status))
559 {
560 /* Failure */
561 BaseSetLastNTError(Status);
562 return FALSE;
563 }
564
565 /* Success */
566 return TRUE;
567 }
568
569 /*
570 * @implemented
571 */
572 BOOL
573 WINAPI
574 DebugSetProcessKillOnExit(IN BOOL KillOnExit)
575 {
576 HANDLE Handle;
577 NTSTATUS Status;
578 ULONG State;
579
580 /* Get the debug object */
581 Handle = DbgUiGetThreadDebugObject();
582 if (!Handle)
583 {
584 /* Fail */
585 BaseSetLastNTError(STATUS_INVALID_HANDLE);
586 return FALSE;
587 }
588
589 /* Now set the kill-on-exit state */
590 State = KillOnExit != 0;
591 Status = NtSetInformationDebugObject(Handle,
592 DebugObjectKillProcessOnExitInformation,
593 &State,
594 sizeof(State),
595 NULL);
596 if (!NT_SUCCESS(Status))
597 {
598 /* Fail */
599 BaseSetLastNTError(Status);
600 return FALSE;
601 }
602
603 /* Success */
604 return TRUE;
605 }
606
607 /*
608 * @implemented
609 */
610 BOOL
611 WINAPI
612 IsDebuggerPresent(VOID)
613 {
614 return (BOOL)NtCurrentPeb()->BeingDebugged;
615 }
616
617 /*
618 * @implemented
619 */
620 BOOL
621 WINAPI
622 WaitForDebugEvent(IN LPDEBUG_EVENT lpDebugEvent,
623 IN DWORD dwMilliseconds)
624 {
625 LARGE_INTEGER WaitTime;
626 PLARGE_INTEGER Timeout;
627 DBGUI_WAIT_STATE_CHANGE WaitStateChange;
628 NTSTATUS Status;
629
630 /* Convert to NT Timeout */
631 Timeout = BaseFormatTimeOut(&WaitTime, dwMilliseconds);
632
633 /* Loop while we keep getting interrupted */
634 do
635 {
636 /* Call the native API */
637 Status = DbgUiWaitStateChange(&WaitStateChange, Timeout);
638 } while ((Status == STATUS_ALERTED) || (Status == STATUS_USER_APC));
639
640 /* Check if the wait failed */
641 if (!(NT_SUCCESS(Status)) || (Status == DBG_UNABLE_TO_PROVIDE_HANDLE))
642 {
643 /* Set the error code and quit */
644 BaseSetLastNTError(Status);
645 return FALSE;
646 }
647
648 /* Check if we timed out */
649 if (Status == STATUS_TIMEOUT)
650 {
651 /* Fail with a timeout error */
652 SetLastError(ERROR_SEM_TIMEOUT);
653 return FALSE;
654 }
655
656 /* Convert the structure */
657 Status = DbgUiConvertStateChangeStructure(&WaitStateChange, lpDebugEvent);
658 if (!NT_SUCCESS(Status))
659 {
660 /* Set the error code and quit */
661 BaseSetLastNTError(Status);
662 return FALSE;
663 }
664
665 /* Check what kind of event this was */
666 switch (lpDebugEvent->dwDebugEventCode)
667 {
668 /* New thread was created */
669 case CREATE_THREAD_DEBUG_EVENT:
670
671 /* Setup the thread data */
672 SaveThreadHandle(lpDebugEvent->dwProcessId,
673 lpDebugEvent->dwThreadId,
674 lpDebugEvent->u.CreateThread.hThread);
675 break;
676
677 /* New process was created */
678 case CREATE_PROCESS_DEBUG_EVENT:
679
680 /* Setup the process data */
681 SaveProcessHandle(lpDebugEvent->dwProcessId,
682 lpDebugEvent->u.CreateProcessInfo.hProcess);
683
684 /* Setup the thread data */
685 SaveThreadHandle(lpDebugEvent->dwProcessId,
686 lpDebugEvent->dwThreadId,
687 lpDebugEvent->u.CreateProcessInfo.hThread);
688 break;
689
690 /* Process was exited */
691 case EXIT_PROCESS_DEBUG_EVENT:
692
693 /* Mark the thread data as such and fall through */
694 MarkProcessHandle(lpDebugEvent->dwProcessId);
695
696 /* Thread was exited */
697 case EXIT_THREAD_DEBUG_EVENT:
698
699 /* Mark the thread data */
700 MarkThreadHandle(lpDebugEvent->dwThreadId);
701 break;
702
703 /* Nothing to do */
704 case EXCEPTION_DEBUG_EVENT:
705 case LOAD_DLL_DEBUG_EVENT:
706 case UNLOAD_DLL_DEBUG_EVENT:
707 case OUTPUT_DEBUG_STRING_EVENT:
708 case RIP_EVENT:
709 break;
710
711 /* Fail anything else */
712 default:
713 return FALSE;
714 }
715
716 /* Return success */
717 return TRUE;
718 }
719
720 /*
721 * @implemented
722 */
723 VOID
724 WINAPI
725 OutputDebugStringA(IN LPCSTR _OutputString)
726 {
727 _SEH2_TRY
728 {
729 ULONG_PTR a_nArgs[2];
730
731 a_nArgs[0] = (ULONG_PTR)(strlen(_OutputString) + 1);
732 a_nArgs[1] = (ULONG_PTR)_OutputString;
733
734 /* send the string to the user-mode debugger */
735 RaiseException(DBG_PRINTEXCEPTION_C, 0, 2, a_nArgs);
736 }
737 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
738 {
739 /* no user-mode debugger: try the systemwide debug message monitor, or the
740 kernel debugger as a last resort */
741
742 /* mutex used to synchronize invocations of OutputDebugString */
743 static HANDLE s_hDBMonMutex = NULL;
744 /* true if we already attempted to open/create the mutex */
745 static BOOL s_bDBMonMutexTriedOpen = FALSE;
746
747 /* local copy of the mutex handle */
748 volatile HANDLE hDBMonMutex = s_hDBMonMutex;
749 /* handle to the Section of the shared buffer */
750 volatile HANDLE hDBMonBuffer = NULL;
751
752 /* pointer to the mapped view of the shared buffer. It consist of the current
753 process id followed by the message string */
754 struct { DWORD ProcessId; CHAR Buffer[1]; } * pDBMonBuffer = NULL;
755
756 /* event: signaled by the debug message monitor when OutputDebugString can write
757 to the shared buffer */
758 volatile HANDLE hDBMonBufferReady = NULL;
759
760 /* event: to be signaled by OutputDebugString when it's done writing to the
761 shared buffer */
762 volatile HANDLE hDBMonDataReady = NULL;
763
764 /* mutex not opened, and no previous attempts to open/create it */
765 if(hDBMonMutex == NULL && !s_bDBMonMutexTriedOpen)
766 {
767 /* open/create the mutex */
768 hDBMonMutex = K32CreateDBMonMutex();
769 /* store the handle */
770 s_hDBMonMutex = hDBMonMutex;
771 }
772
773 _SEH2_TRY
774 {
775 volatile PCHAR a_cBuffer = NULL;
776
777 /* opening the mutex failed */
778 if(hDBMonMutex == NULL)
779 {
780 /* remember next time */
781 s_bDBMonMutexTriedOpen = TRUE;
782 }
783 /* opening the mutex succeeded */
784 else
785 {
786 do
787 {
788 /* synchronize with other invocations of OutputDebugString */
789 WaitForSingleObject(hDBMonMutex, INFINITE);
790
791 /* buffer of the system-wide debug message monitor */
792 hDBMonBuffer = OpenFileMappingW(SECTION_MAP_WRITE, FALSE, L"DBWIN_BUFFER");
793
794 /* couldn't open the buffer: send the string to the kernel debugger */
795 if(hDBMonBuffer == NULL) break;
796
797 /* map the buffer */
798 pDBMonBuffer = MapViewOfFile(hDBMonBuffer,
799 SECTION_MAP_READ | SECTION_MAP_WRITE,
800 0,
801 0,
802 0);
803
804 /* couldn't map the buffer: send the string to the kernel debugger */
805 if(pDBMonBuffer == NULL) break;
806
807 /* open the event signaling that the buffer can be accessed */
808 hDBMonBufferReady = OpenEventW(SYNCHRONIZE, FALSE, L"DBWIN_BUFFER_READY");
809
810 /* couldn't open the event: send the string to the kernel debugger */
811 if(hDBMonBufferReady == NULL) break;
812
813 /* open the event to be signaled when the buffer has been filled */
814 hDBMonDataReady = OpenEventW(EVENT_MODIFY_STATE, FALSE, L"DBWIN_DATA_READY");
815 }
816 while(0);
817
818 /* we couldn't connect to the system-wide debug message monitor: send the
819 string to the kernel debugger */
820 if(hDBMonDataReady == NULL) ReleaseMutex(hDBMonMutex);
821 }
822
823 _SEH2_TRY
824 {
825 /* size of the current output block */
826 volatile SIZE_T nRoundLen;
827
828 /* size of the remainder of the string */
829 volatile SIZE_T nOutputStringLen;
830
831 /* output the whole string */
832 nOutputStringLen = strlen(_OutputString);
833
834 do
835 {
836 /* we're connected to the debug monitor:
837 write the current block to the shared buffer */
838 if(hDBMonDataReady)
839 {
840 /* wait a maximum of 10 seconds for the debug monitor
841 to finish processing the shared buffer */
842 if(WaitForSingleObject(hDBMonBufferReady, 10000) != WAIT_OBJECT_0)
843 {
844 /* timeout or failure: give up */
845 break;
846 }
847
848 /* write the process id into the buffer */
849 pDBMonBuffer->ProcessId = GetCurrentProcessId();
850
851 /* write only as many bytes as they fit in the buffer */
852 if(nOutputStringLen > (PAGE_SIZE - sizeof(DWORD) - 1))
853 nRoundLen = PAGE_SIZE - sizeof(DWORD) - 1;
854 else
855 nRoundLen = nOutputStringLen;
856
857 /* copy the current block into the buffer */
858 memcpy(pDBMonBuffer->Buffer, _OutputString, nRoundLen);
859
860 /* null-terminate the current block */
861 pDBMonBuffer->Buffer[nRoundLen] = 0;
862
863 /* signal that the data contains meaningful data and can be read */
864 SetEvent(hDBMonDataReady);
865 }
866 /* else, send the current block to the kernel debugger */
867 else
868 {
869 /* output in blocks of 512 characters */
870 a_cBuffer = (CHAR*)HeapAlloc(GetProcessHeap(), 0, 512);
871
872 if (!a_cBuffer)
873 {
874 DbgPrint("OutputDebugStringA: Failed\n");
875 break;
876 }
877
878 /* write a maximum of 511 bytes */
879 if(nOutputStringLen > 510)
880 nRoundLen = 510;
881 else
882 nRoundLen = nOutputStringLen;
883
884 /* copy the current block */
885 memcpy(a_cBuffer, _OutputString, nRoundLen);
886
887 /* null-terminate the current block */
888 a_cBuffer[nRoundLen] = 0;
889
890 /* send the current block to the kernel debugger */
891 DbgPrint("%s", a_cBuffer);
892
893 if (a_cBuffer)
894 {
895 HeapFree(GetProcessHeap(), 0, a_cBuffer);
896 a_cBuffer = NULL;
897 }
898 }
899
900 /* move to the next block */
901 _OutputString += nRoundLen;
902 nOutputStringLen -= nRoundLen;
903 }
904 /* repeat until the string has been fully output */
905 while (nOutputStringLen > 0);
906 }
907 /* ignore access violations and let other exceptions fall through */
908 _SEH2_EXCEPT((_SEH2_GetExceptionCode() == STATUS_ACCESS_VIOLATION) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
909 {
910 if (a_cBuffer)
911 HeapFree(GetProcessHeap(), 0, a_cBuffer);
912
913 /* string copied verbatim from Microsoft's kernel32.dll */
914 DbgPrint("\nOutputDebugString faulted during output\n");
915 }
916 _SEH2_END;
917 }
918 _SEH2_FINALLY
919 {
920 /* close all the still open resources */
921 if(hDBMonBufferReady) CloseHandle(hDBMonBufferReady);
922 if(pDBMonBuffer) UnmapViewOfFile(pDBMonBuffer);
923 if(hDBMonBuffer) CloseHandle(hDBMonBuffer);
924 if(hDBMonDataReady) CloseHandle(hDBMonDataReady);
925
926 /* leave the critical section */
927 if(hDBMonDataReady != NULL)
928 ReleaseMutex(hDBMonMutex);
929 }
930 _SEH2_END;
931 }
932 _SEH2_END;
933 }
934
935 /*
936 * @implemented
937 */
938 VOID
939 WINAPI
940 OutputDebugStringW(IN LPCWSTR OutputString)
941 {
942 UNICODE_STRING UnicodeString;
943 ANSI_STRING AnsiString;
944 NTSTATUS Status;
945
946 /* convert the string in ANSI */
947 RtlInitUnicodeString(&UnicodeString, OutputString);
948 Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE);
949
950 /* OutputDebugStringW always prints something, even if conversion fails */
951 if (!NT_SUCCESS(Status)) AnsiString.Buffer = "";
952
953 /* Output the converted string */
954 OutputDebugStringA(AnsiString.Buffer);
955
956 /* free the converted string */
957 if (NT_SUCCESS(Status)) RtlFreeAnsiString(&AnsiString);
958 }
959
960 /* EOF */