/* * Checks if RedZoneHandler_FlagTopConsumer() updates the CurrentVersion and * latestRunawayVersion */ void test__RedZoneHandler_FlagTopConsumer__UpdatesEventVersions(void **state) { /* Make sure the RedZoneHandler_FlagTopConsumer code is exercised */ vmemTrackerInited = true; CreateSessionStateArray(1); /* Make sure MySessionState is valid */ SessionState *one = AcquireSessionState(1 /* sessionId */, 100 /* vmem */, 1 /* activeProcessCount */); /* Ensure we can detect runaway sessions */ *isRunawayDetector = 0; will_be_called_count(LWLockAcquire, 1); will_be_called_count(LWLockRelease, 1); expect_any_count(LWLockAcquire, l, 1); expect_any_count(LWLockAcquire, mode, 1); expect_any_count(LWLockRelease, l, 1); static EventVersion fakeLatestRunawayVersion = 0; static EventVersion fakeCurrentVersion = 1; latestRunawayVersion = &fakeLatestRunawayVersion; CurrentVersion = &fakeCurrentVersion; RedZoneHandler_FlagTopConsumer(); assert_true(one->runawayStatus == RunawayStatus_PrimaryRunawaySession); /* Verify that the event versions were properly updated */ assert_true(*CurrentVersion == 3 && *latestRunawayVersion == 2); DestroySessionStateArray(); }
/* * Checks if RedZoneHandler_FlagTopConsumer() allows only one detector * at a time */ void test__RedZoneHandler_FlagTopConsumer__SingletonDetector(void **state) { /* Make sure the code is exercised */ vmemTrackerInited = true; /* Ensure non-null MySessionState */ MySessionState = 0x1234; static uint32 fakeIsRunawayDetector = 0; isRunawayDetector = &fakeIsRunawayDetector; /* We already have a runaway detector */ *isRunawayDetector = 1; /* * This will return without attempting to detecting any runaway session. * This is tested from the fact that it is not trying to call LWLocAcquire */ RedZoneHandler_FlagTopConsumer(); }
/* * In a red-zone this method identifies the top vmem consuming session, * and requests it to cleanup. If the red-zone handler determines itself * as the runaway session, it also starts the cleanup. */ void RedZoneHandler_DetectRunawaySession() { /* * InterruptHoldoffCount > 0 indicates we are in a sensitive code path that doesn't * like a control flow disruption as may happen from a pending die/cancel interrupt. * As we may eventually ERROR out from this method (during RunawayCleaner_StartCleanup) * we want to make sure that HOLD_INTERRUPTS() was not called (i.e., InterruptHoldoffCount == 0). * * What happens if we don't check for InterruptHoldoffCount? One example is LWLockAcquire() * which calls HOLD_INTERRUPTS() to ensure that no unexpected control * flow disruption happens because of FATAL/ERROR as done from die/cancel interrupt * handler. If we ignore InterruptHoldoffCount, the PGSemaphoreLock() (called from LWLockAcquire) * would call CHECK_FOR_INTERRUPTS() and we may throw ERROR if the current session is a runaway. * Unfortunately, LWLockAcquire shares the semaphore with the regular lock manager and * ProcWaitForSignal. Therefore, LWLockAcquire may wake up multiple times during its wait * for a semaphore which may not relate to an actual LWLock release. This requires LWLockAcquire * to keep track of how many of those false wake events it has consumed (by decrementing semaphore * when it shouldn't have done so) and LWLockAcquire rollback the semaphore decrements for * the irrelevant wake up events by re-incrementing once it actually acquires the lock. * Therefore, an unexpected control flow out of the LWLockAcquire before it properly rolled back * may prevent the LWLockAcquire to rollback the false wake events. Although we do call LWLockRelease * during an error handling, that doesn't guarantee that the falsely consumed semaphore wake * events would be rolled back (i.e., semaphore does not get re-incremented during error handling) as * done at the end of LWLockAcquire. This may cause the semaphore to never wake up other waiting * processes and therefore may cause other processes to hang perpetually. */ if (!RedZoneHandler_IsVmemRedZone() || InterruptHoldoffCount > 0 || CritSectionCount > 0) { return; } /* We don't support runaway detection/termination from non-owner thread */ Assert(MemoryProtection_IsOwnerThread()); Assert(gp_mp_inited); RedZoneHandler_FlagTopConsumer(); RunawayCleaner_StartCleanup(); }
/* * Checks if RedZoneHandler_FlagTopConsumer() reactivates the runaway detector * if there is no active session */ void test__RedZoneHandler_FlagTopConsumer__ReactivatesDetectorIfNoActiveSession(void **state) { /* Make sure the RedZoneHandler_FlagTopConsumer code is exercised */ vmemTrackerInited = true; CreateSessionStateArray(4); /* Make sure MySessionState is valid */ SessionState *one = AcquireSessionState(1 /* sessionId */, 100 /* vmem */, 0 /* activeProcessCount */); SessionState *two = AcquireSessionState(2, 101, 0); SessionState *three = AcquireSessionState(3, 100, 0); SessionState *four = AcquireSessionState(4, 99, 0); /* Ensure we can detect runaway sessions */ *isRunawayDetector = 0; will_be_called_count(LWLockAcquire, 1); will_be_called_count(LWLockRelease, 1); expect_any_count(LWLockAcquire, l, 1); expect_any_count(LWLockAcquire, mode, 1); expect_any_count(LWLockRelease, l, 1); static EventVersion fakeLatestRunawayVersion = 0; static EventVersion fakeCurrentVersion = 1; latestRunawayVersion = &fakeLatestRunawayVersion; CurrentVersion = &fakeCurrentVersion; RedZoneHandler_FlagTopConsumer(); /* None of them could be detected as runaway as all of them are inactive sessions */ assert_true(one->runawayStatus == RunawayStatus_NotRunaway && two->runawayStatus == RunawayStatus_NotRunaway && three->runawayStatus == RunawayStatus_NotRunaway && four->runawayStatus == RunawayStatus_NotRunaway); assert_true(*isRunawayDetector == 0); DestroySessionStateArray(); }
/* * Checks if RedZoneHandler_FlagTopConsumer() ignores the idle sessions * even if they are the top consumer */ void test__RedZoneHandler_FlagTopConsumer__IgnoresIdleSession(void **state) { /* Make sure the RedZoneHandler_FlagTopConsumer code is exercised */ vmemTrackerInited = true; CreateSessionStateArray(4); /* Make sure MySessionState is valid */ SessionState *one = AcquireSessionState(1 /* sessionId */, 100 /* vmem */, 1 /* activeProcessCount */); SessionState *two = AcquireSessionState(2, 101, 0); SessionState *three = AcquireSessionState(3, 100, 0); SessionState *four = AcquireSessionState(4, 99, 1); /* Ensure we can detect runaway sessions */ *isRunawayDetector = 0; will_be_called_count(LWLockAcquire, 1); will_be_called_count(LWLockRelease, 1); expect_any_count(LWLockAcquire, l, 1); expect_any_count(LWLockAcquire, mode, 1); expect_any_count(LWLockRelease, l, 1); static EventVersion fakeLatestRunawayVersion = 0; static EventVersion fakeCurrentVersion = 1; latestRunawayVersion = &fakeLatestRunawayVersion; CurrentVersion = &fakeCurrentVersion; RedZoneHandler_FlagTopConsumer(); assert_true(one->runawayStatus == RunawayStatus_SecondaryRunawaySession && two->runawayStatus == RunawayStatus_NotRunaway && three->runawayStatus == RunawayStatus_NotRunaway /* We will encounter three first, but it doesn't have active process. So, RDT will ignore it. */ && four->runawayStatus == RunawayStatus_NotRunaway); DestroySessionStateArray(); }
/* * Checks if RedZoneHandler_FlagTopConsumer() finds the top consumer */ void test__RedZoneHandler_FlagTopConsumer__FindsTopConsumer(void **state) { /* Make sure the RedZoneHandler_FlagTopConsumer code is exercised */ vmemTrackerInited = true; CreateSessionStateArray(4); /* Make sure MySessionState is valid */ SessionState *one = AcquireSessionState(1 /* sessionId */, 100 /* vmem */, 1 /* activeProcessCount */); SessionState *two = AcquireSessionState(2, 101, 1); SessionState *three = AcquireSessionState(3, 101, 1); SessionState *four = AcquireSessionState(4, 99, 1); /* Ensure we can detect runaway sessions */ *isRunawayDetector = 0; will_be_called_count(LWLockAcquire, 1); will_be_called_count(LWLockRelease, 1); expect_any_count(LWLockAcquire, l, 1); expect_any_count(LWLockAcquire, mode, 1); expect_any_count(LWLockRelease, l, 1); static EventVersion fakeLatestRunawayVersion = 0; static EventVersion fakeCurrentVersion = 1; latestRunawayVersion = &fakeLatestRunawayVersion; CurrentVersion = &fakeCurrentVersion; RedZoneHandler_FlagTopConsumer(); assert_true(one->runawayStatus == RunawayStatus_NotRunaway && two->runawayStatus == RunawayStatus_NotRunaway /* three is tied with two. So, won't be flagged */ && three->runawayStatus == RunawayStatus_PrimaryRunawaySession /* First detected max consumer is flagged (note the usedList is reversed, so "three" will be ahead of "two") */ && four->runawayStatus == RunawayStatus_NotRunaway); DestroySessionStateArray(); }