| #include <stdarg.h> |
| #include <stddef.h> |
| #include <setjmp.h> |
| #include "cmockery.h" |
| |
| #include "../redzone_handler.c" |
| |
| #define EXPECT_EREPORT(LOG_LEVEL) \ |
| expect_any(errstart, elevel); \ |
| expect_any(errstart, domain); \ |
| if (LOG_LEVEL < ERROR) \ |
| { \ |
| will_return(errstart, false); \ |
| } \ |
| else \ |
| { \ |
| will_return_with_sideeffect(errstart, false, &_ExceptionalCondition, NULL);\ |
| } \ |
| |
| /* isRunawayDetector assumes the address of this variable */ |
| static uint32 fakeIsRunawayDetector = 0; |
| extern bool sessionStateInited; |
| |
| #undef PG_RE_THROW |
| #define PG_RE_THROW() siglongjmp(*PG_exception_stack, 1) |
| |
| /* |
| * This method will emulate the real ExceptionalCondition |
| * function by re-throwing the exception, essentially falling |
| * back to the next available PG_CATCH(); |
| */ |
| static void |
| _ExceptionalCondition() |
| { |
| PG_RE_THROW(); |
| } |
| |
| /* Creates a SessionStateArray of the specified number of entry */ |
| static void |
| CreateSessionStateArray(int numEntries) |
| { |
| MaxBackends = numEntries; |
| |
| IsUnderPostmaster = false; |
| |
| assert_true(NULL == AllSessionStateEntries); |
| |
| SessionStateArray *fakeSessionStateArray = NULL; |
| fakeSessionStateArray = malloc(SessionState_ShmemSize()); |
| |
| bool found = false; |
| will_return(ShmemInitStruct, fakeSessionStateArray); |
| will_assign_value(ShmemInitStruct, foundPtr, found); |
| |
| expect_any_count(ShmemInitStruct, name, 1); |
| expect_any_count(ShmemInitStruct, size, 1); |
| expect_any_count(ShmemInitStruct, foundPtr, 1); |
| |
| SessionState_ShmemInit(); |
| |
| /* The lookup should always work, whether under postmaster or not */ |
| assert_true(AllSessionStateEntries == fakeSessionStateArray); |
| } |
| |
| /* Frees a previously created SessionStateArray */ |
| static void |
| DestroySessionStateArray() |
| { |
| assert_true(NULL != AllSessionStateEntries); |
| free((void *) AllSessionStateEntries); |
| AllSessionStateEntries = NULL; |
| } |
| |
| /* |
| * Acquires a SessionState entry for the specified sessionid. If an existing entry |
| * is found, this method reuses that entry |
| */ |
| static SessionState * |
| AcquireSessionState(int sessionId, int vmem, int activeProcessCount) |
| { |
| will_be_called_count(LWLockAcquire, 1); |
| will_be_called_count(LWLockRelease, 1); |
| expect_any_count(LWLockAcquire, lock, 1); |
| expect_any_count(LWLockAcquire, mode, 1); |
| expect_any_count(LWLockRelease, lock, 1); |
| |
| /* Keep the assertions happy */ |
| gp_session_id = sessionId; |
| sessionStateInited = false; |
| MySessionState = NULL; |
| |
| EXPECT_EREPORT(gp_sessionstate_loglevel); |
| SessionState_Init(); |
| |
| if (vmem >= 0) |
| { |
| MySessionState->sessionVmem = vmem; |
| } |
| |
| if (activeProcessCount >= 0) |
| { |
| MySessionState->activeProcessCount = activeProcessCount; |
| } |
| |
| return (SessionState *) MySessionState; |
| } |
| /* |
| * Checks if RedZoneHandler_ShmemInit() properly initializes the global variables |
| * as the postmaster |
| */ |
| static void |
| test__RedZoneHandler_ShmemInit__InitializesGlobalVarsWhenPostmaster(void **state) |
| { |
| vmemTrackerInited = false; |
| IsUnderPostmaster = false; |
| |
| /* Assign weird value to test the re-initialization */ |
| fakeIsRunawayDetector = 1234; |
| isRunawayDetector = NULL; |
| |
| expect_any_count(ShmemInitStruct, name, 3); |
| expect_any_count(ShmemInitStruct, size, 3); |
| expect_any_count(ShmemInitStruct, foundPtr, 3); |
| will_assign_value(ShmemInitStruct, foundPtr, (bool) false); |
| will_assign_value(ShmemInitStruct, foundPtr, (bool) false); |
| will_assign_value(ShmemInitStruct, foundPtr, (bool) false); |
| will_return_count(ShmemInitStruct, &fakeIsRunawayDetector, 3); |
| |
| |
| will_return(VmemTracker_ConvertVmemMBToChunks, 0); |
| expect_any(VmemTracker_ConvertVmemMBToChunks, mb); |
| |
| /* |
| * When vmem limit is not activated or runaway_detector_activation_percent is |
| * set to 0, red zone should be very high (i.e., red-zone will be disabled). |
| */ |
| runaway_detector_activation_percent = 0; |
| RedZoneHandler_ShmemInit(); |
| assert_true(isRunawayDetector == &fakeIsRunawayDetector); |
| assert_true(redZoneChunks == INT32_MAX); |
| assert_true(*isRunawayDetector == 0); |
| |
| /* |
| * When the activation percent is set to 100, we will not even attempt calculating |
| * the redZoneChunks and instead assign INT32_MAX directly. Note, we don't even |
| * call VmemTracker_ConvertVmemMBToChunks() |
| */ |
| runaway_detector_activation_percent = 100; |
| redZoneChunks = 0; |
| RedZoneHandler_ShmemInit(); |
| assert_true(redZoneChunks == INT32_MAX); |
| |
| runaway_detector_activation_percent = 80; |
| RedZoneHandler_ShmemInit(); |
| } |
| |
| /* |
| * Checks if RedZoneHandler_ShmemInit() properly initializes the global variables |
| * when under postmaster |
| */ |
| static void |
| test__RedZoneHandler_ShmemInit__InitializesUnderPostmaster(void **state) |
| { |
| vmemTrackerInited = false; |
| IsUnderPostmaster = true; |
| |
| /* Assign weird value to test the re-initialization */ |
| fakeIsRunawayDetector = 1234; |
| isRunawayDetector = NULL; |
| |
| expect_any(ShmemInitStruct, name); |
| expect_any(ShmemInitStruct, size); |
| expect_any(ShmemInitStruct, foundPtr); |
| will_assign_value(ShmemInitStruct, foundPtr, (bool) true); |
| will_return(ShmemInitStruct, &fakeIsRunawayDetector); |
| |
| /* For testing that we don't change this value */ |
| redZoneChunks = 1234; |
| RedZoneHandler_ShmemInit(); |
| |
| assert_true(isRunawayDetector == &fakeIsRunawayDetector); |
| assert_true(redZoneChunks == 1234); |
| assert_true(*isRunawayDetector == 1234); |
| } |
| |
| /* |
| * Checks if RedZoneHandler_IsVmemRedZone() properly identifies red zone |
| */ |
| static void |
| test__RedZoneHandler_IsVmemRedZone__ProperlyIdentifiesRedZone(void **state) |
| { |
| vmemTrackerInited = false; |
| |
| /* No red zone detection if vmem tracker is not initialized */ |
| assert_false(RedZoneHandler_IsVmemRedZone()); |
| |
| vmemTrackerInited = true; |
| |
| static int32 fakeSegmentVmemChunks = 0; |
| segmentVmemChunks = &fakeSegmentVmemChunks; |
| |
| redZoneChunks = INT32_MAX; |
| *segmentVmemChunks = INT32_MAX; |
| /* Both segment vmem and red zone is INT32_MAX. It's not a red-zone */ |
| assert_false(RedZoneHandler_IsVmemRedZone()); |
| |
| /* 100 chunks */ |
| *segmentVmemChunks = 100; |
| redZoneChunks = 80; |
| /* segmentVmemChunks exceeds redZoneChunks. So, should be red zone */ |
| assert_true(RedZoneHandler_IsVmemRedZone()); |
| |
| vmemTrackerInited = false; |
| /* |
| * segmentVmemChunks exceeds redZoneChunks. But vmem tracker is not |
| * initialized. Therefore, no red zone detection |
| */ |
| assert_false(RedZoneHandler_IsVmemRedZone()); |
| } |
| |
| /* |
| * Checks if RedZoneHandler_FlagTopConsumer() allows only one detector |
| * at a time |
| */ |
| static void |
| test__RedZoneHandler_FlagTopConsumer__SingletonDetector(void **state) |
| { |
| /* Make sure the code is exercised */ |
| vmemTrackerInited = true; |
| |
| /* Ensure non-null MySessionState */ |
| MySessionState = (SessionState *) 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(); |
| } |
| |
| /* |
| * Checks if RedZoneHandler_FlagTopConsumer() finds the top consumer |
| */ |
| static 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, lock, 1); |
| expect_any_count(LWLockAcquire, mode, 1); |
| expect_any_count(LWLockRelease, lock, 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(); |
| } |
| |
| /* |
| * Checks if RedZoneHandler_FlagTopConsumer() ignores the idle sessions |
| * even if they are the top consumer |
| */ |
| static 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, lock, 1); |
| expect_any_count(LWLockAcquire, mode, 1); |
| expect_any_count(LWLockRelease, lock, 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() reactivates the runaway detector |
| * if there is no active session |
| */ |
| static 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, lock, 1); |
| expect_any_count(LWLockAcquire, mode, 1); |
| expect_any_count(LWLockRelease, lock, 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() updates the CurrentVersion and |
| * latestRunawayVersion |
| */ |
| static 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, lock, 1); |
| expect_any_count(LWLockAcquire, mode, 1); |
| expect_any_count(LWLockRelease, lock, 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(); |
| } |
| |
| int |
| main(int argc, char* argv[]) |
| { |
| cmockery_parse_arguments(argc, argv); |
| |
| const UnitTest tests[] = { |
| unit_test(test__RedZoneHandler_ShmemInit__InitializesGlobalVarsWhenPostmaster), |
| unit_test(test__RedZoneHandler_ShmemInit__InitializesUnderPostmaster), |
| unit_test(test__RedZoneHandler_IsVmemRedZone__ProperlyIdentifiesRedZone), |
| unit_test(test__RedZoneHandler_FlagTopConsumer__SingletonDetector), |
| unit_test(test__RedZoneHandler_FlagTopConsumer__FindsTopConsumer), |
| unit_test(test__RedZoneHandler_FlagTopConsumer__IgnoresIdleSession), |
| unit_test(test__RedZoneHandler_FlagTopConsumer__ReactivatesDetectorIfNoActiveSession), |
| unit_test(test__RedZoneHandler_FlagTopConsumer__UpdatesEventVersions), |
| }; |
| |
| return run_tests(tests); |
| } |