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