|
34 | 34 | #include "finalizerthread.h" |
35 | 35 | #include "threadsuspend.h" |
36 | 36 | #include <minipal/memorybarrierprocesswide.h> |
| 37 | +#include "string.h" |
| 38 | +#include "sstring.h" |
| 39 | +#include "array.h" |
| 40 | +#include "eepolicy.h" |
| 41 | +#include <minipal/cpuid.h> |
37 | 42 |
|
38 | 43 | #ifdef FEATURE_COMINTEROP |
39 | 44 | #include "comcallablewrapper.h" |
@@ -564,6 +569,183 @@ FCIMPL3(VOID, Buffer::BulkMoveWithWriteBarrier, void *dst, void *src, size_t byt |
564 | 569 | } |
565 | 570 | FCIMPLEND |
566 | 571 |
|
| 572 | +// |
| 573 | +// EnvironmentNative |
| 574 | +// |
| 575 | +extern "C" VOID QCALLTYPE Environment_Exit(INT32 exitcode) |
| 576 | +{ |
| 577 | + QCALL_CONTRACT; |
| 578 | + |
| 579 | + BEGIN_QCALL; |
| 580 | + |
| 581 | + // The exit code for the process is communicated in one of two ways. If the |
| 582 | + // entrypoint returns an 'int' we take that. Otherwise we take a latched |
| 583 | + // process exit code. This can be modified by the app via setting |
| 584 | + // Environment's ExitCode property. |
| 585 | + SetLatchedExitCode(exitcode); |
| 586 | + |
| 587 | + ForceEEShutdown(); |
| 588 | + |
| 589 | + END_QCALL; |
| 590 | +} |
| 591 | + |
| 592 | +FCIMPL1(VOID,EnvironmentNative::SetExitCode,INT32 exitcode) |
| 593 | +{ |
| 594 | + FCALL_CONTRACT; |
| 595 | + |
| 596 | + // The exit code for the process is communicated in one of two ways. If the |
| 597 | + // entrypoint returns an 'int' we take that. Otherwise we take a latched |
| 598 | + // process exit code. This can be modified by the app via setting |
| 599 | + // Environment's ExitCode property. |
| 600 | + SetLatchedExitCode(exitcode); |
| 601 | +} |
| 602 | +FCIMPLEND |
| 603 | + |
| 604 | +FCIMPL0(INT32, EnvironmentNative::GetExitCode) |
| 605 | +{ |
| 606 | + FCALL_CONTRACT; |
| 607 | + |
| 608 | + // Return whatever has been latched so far. This is uninitialized to 0. |
| 609 | + return GetLatchedExitCode(); |
| 610 | +} |
| 611 | +FCIMPLEND |
| 612 | + |
| 613 | +extern "C" INT32 QCALLTYPE Environment_GetProcessorCount() |
| 614 | +{ |
| 615 | + QCALL_CONTRACT; |
| 616 | + |
| 617 | + INT32 processorCount = 0; |
| 618 | + |
| 619 | + BEGIN_QCALL; |
| 620 | + |
| 621 | + processorCount = GetCurrentProcessCpuCount(); |
| 622 | + |
| 623 | + END_QCALL; |
| 624 | + |
| 625 | + return processorCount; |
| 626 | +} |
| 627 | + |
| 628 | +struct FindFailFastCallerStruct { |
| 629 | + StackCrawlMark* pStackMark; |
| 630 | + UINT_PTR retAddress; |
| 631 | +}; |
| 632 | + |
| 633 | +// This method is called by the GetMethod function and will crawl backward |
| 634 | +// up the stack for integer methods. |
| 635 | +static StackWalkAction FindFailFastCallerCallback(CrawlFrame* frame, VOID* data) { |
| 636 | + CONTRACTL |
| 637 | + { |
| 638 | + NOTHROW; |
| 639 | + GC_NOTRIGGER; |
| 640 | + MODE_ANY; |
| 641 | + } |
| 642 | + CONTRACTL_END; |
| 643 | + |
| 644 | + FindFailFastCallerStruct* pFindCaller = (FindFailFastCallerStruct*) data; |
| 645 | + |
| 646 | + // The check here is between the address of a local variable |
| 647 | + // (the stack mark) and a pointer to the EIP for a frame |
| 648 | + // (which is actually the pointer to the return address to the |
| 649 | + // function from the previous frame). So we'll actually notice |
| 650 | + // which frame the stack mark was in one frame later. This is |
| 651 | + // fine since we only implement LookForMyCaller. |
| 652 | + _ASSERTE(*pFindCaller->pStackMark == LookForMyCaller); |
| 653 | + if (!frame->IsInCalleesFrames(pFindCaller->pStackMark)) |
| 654 | + return SWA_CONTINUE; |
| 655 | + |
| 656 | + pFindCaller->retAddress = GetControlPC(frame->GetRegisterSet()); |
| 657 | + return SWA_ABORT; |
| 658 | +} |
| 659 | + |
| 660 | +static thread_local int8_t alreadyFailing = 0; |
| 661 | + |
| 662 | +extern "C" void QCALLTYPE Environment_FailFast(QCall::StackCrawlMarkHandle mark, PCWSTR message, QCall::ObjectHandleOnStack exception, PCWSTR errorSource) |
| 663 | +{ |
| 664 | + QCALL_CONTRACT; |
| 665 | + |
| 666 | + BEGIN_QCALL; |
| 667 | + |
| 668 | + GCX_COOP(); |
| 669 | + |
| 670 | + FindFailFastCallerStruct findCallerData; |
| 671 | + findCallerData.pStackMark = mark; |
| 672 | + findCallerData.retAddress = 0; |
| 673 | + GetThread()->StackWalkFrames(FindFailFastCallerCallback, &findCallerData, FUNCTIONSONLY | QUICKUNWIND); |
| 674 | + |
| 675 | + if (message == NULL || message[0] == W('\0')) |
| 676 | + { |
| 677 | + OutputDebugString(W("CLR: Managed code called FailFast without specifying a reason.\r\n")); |
| 678 | + } |
| 679 | + else |
| 680 | + { |
| 681 | + OutputDebugString(W("CLR: Managed code called FailFast.\r\n")); |
| 682 | + OutputDebugString(message); |
| 683 | + OutputDebugString(W("\r\n")); |
| 684 | + } |
| 685 | + |
| 686 | + LPCWSTR argExceptionString = NULL; |
| 687 | + StackSString msg; |
| 688 | + // Because Environment_FailFast should kill the process, any subsequent calls are likely nested call from managed while formatting the exception message or the stack trace. |
| 689 | + // Only collect exception string if this is the first attempt to fail fast on this thread. |
| 690 | + alreadyFailing++; |
| 691 | + if (alreadyFailing != 1) |
| 692 | + { |
| 693 | + argExceptionString = W("Environment.FailFast called recursively."); |
| 694 | + } |
| 695 | + else if (exception.Get() != NULL) |
| 696 | + { |
| 697 | + GetExceptionMessage(exception.Get(), msg); |
| 698 | + argExceptionString = msg.GetUnicode(); |
| 699 | + } |
| 700 | + |
| 701 | + Thread *pThread = GetThread(); |
| 702 | + |
| 703 | +#ifndef TARGET_UNIX |
| 704 | + // If we have the exception object, then try to setup |
| 705 | + // the watson bucket if it has any details. |
| 706 | + // On CoreCLR, Watson may not be enabled. Thus, we should |
| 707 | + // skip this, if required. |
| 708 | + if (IsWatsonEnabled()) |
| 709 | + { |
| 710 | + if ((exception.Get() == NULL) || !SetupWatsonBucketsForFailFast((EXCEPTIONREF)exception.Get())) |
| 711 | + { |
| 712 | + PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pThread->GetExceptionState()->GetUEWatsonBucketTracker(); |
| 713 | + _ASSERTE(pUEWatsonBucketTracker != NULL); |
| 714 | + pUEWatsonBucketTracker->SaveIpForWatsonBucket(findCallerData.retAddress); |
| 715 | + pUEWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::FatalError, pThread, NULL); |
| 716 | + if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL) |
| 717 | + { |
| 718 | + pUEWatsonBucketTracker->ClearWatsonBucketDetails(); |
| 719 | + } |
| 720 | + } |
| 721 | + } |
| 722 | +#endif // !TARGET_UNIX |
| 723 | + |
| 724 | + // stash the user-provided exception object. this will be used as |
| 725 | + // the inner exception object to the FatalExecutionEngineException. |
| 726 | + if (exception.Get() != NULL) |
| 727 | + pThread->SetLastThrownObject(exception.Get()); |
| 728 | + |
| 729 | + EEPolicy::HandleFatalError(COR_E_FAILFAST, findCallerData.retAddress, message, NULL, errorSource, argExceptionString); |
| 730 | + |
| 731 | + END_QCALL; |
| 732 | +} |
| 733 | + |
| 734 | +#if defined(TARGET_X86) || defined(TARGET_AMD64) |
| 735 | + |
| 736 | +extern "C" void QCALLTYPE X86BaseCpuId(int cpuInfo[4], int functionId, int subFunctionId) |
| 737 | +{ |
| 738 | + QCALL_CONTRACT; |
| 739 | + |
| 740 | + BEGIN_QCALL; |
| 741 | + |
| 742 | + __cpuidex(cpuInfo, functionId, subFunctionId); |
| 743 | + |
| 744 | + END_QCALL; |
| 745 | +} |
| 746 | + |
| 747 | +#endif // defined(TARGET_X86) || defined(TARGET_AMD64) |
| 748 | + |
567 | 749 | // |
568 | 750 | // ObjectNative |
569 | 751 | // |
|
0 commit comments