diff --git a/modules/cfe_assert/inc/cfe_assert.h b/modules/cfe_assert/inc/cfe_assert.h index 082b0872e..a464432ac 100644 --- a/modules/cfe_assert/inc/cfe_assert.h +++ b/modules/cfe_assert/inc/cfe_assert.h @@ -163,6 +163,23 @@ typedef void (*CFE_Assert_StatusCallback_t)(uint8 MessageType, const char *Prefi */ #define CFE_Assert_STATUS_STORE(FN) CFE_Assert_Status_Store(FN, __FILE__, __LINE__, #FN) +/** +** \brief Check if the stored status matches a possible value +** +** Allows the caller to check the value from a previous invocation of CFE_Assert_STATUS_STORE() +** without directly asserting on the value or changing test state. +** +** This may be useful in situations where a large volume of tests are being performed, such as +** during performance or load testing, where reporting all "PASS" cases may add signficant +** extra CPU usage and log volume, interfering with the result. This macro can be followed +** or combined with CFE_Assert_STATUS_MAY_BE / CFE_Assert_STATUS_MUST_BE to do actual reporting. + +** \sa #CFE_Assert_STATUS_STORE, #CFE_Assert_STATUS_MAY_BE, #CFE_Assert_STATUS_MUST_BE +** +** \returns Actual CFE_Status_t value from the call +*/ +#define CFE_Assert_STATUS_SILENTCHECK(expected) CFE_Assert_Status_SilentCheck(expected) + /*****************************************************************************/ /** ** \brief Retroactively check for an acceptable status value from CFE_Assert_STATUS_STORE @@ -357,6 +374,17 @@ bool CFE_Assert_StatusCheck(CFE_Status_t Status, bool ExpectSuccess, UtAssert_Ca */ CFE_Status_t CFE_Assert_Status_Store(CFE_Status_t Status, const char *File, uint32 Line, const char *Text); +/** +** \brief Helper function to silently check status of a previously stored result +** +** \par Description +** This helper function will check the status previously stored to a +** temporary holding area, but does not assert on it. +** +** \returns true if status code matched, false if it did not match. +*/ +bool CFE_Assert_Status_SilentCheck(CFE_Status_t Status); + /** ** \brief Helper function for nominal CFE calls with deferred check ** diff --git a/modules/cfe_assert/src/cfe_assert_runner.c b/modules/cfe_assert/src/cfe_assert_runner.c index 5c5216d23..74a0b53a7 100644 --- a/modules/cfe_assert/src/cfe_assert_runner.c +++ b/modules/cfe_assert/src/cfe_assert_runner.c @@ -111,6 +111,11 @@ CFE_Status_t CFE_Assert_Status_Store(CFE_Status_t Status, const char *File, uint return Status; } +bool CFE_Assert_Status_SilentCheck(CFE_Status_t Status) +{ + return (Status == CFE_Assert_Global.StoredStatus); +} + bool CFE_Assert_Status_DeferredCheck(CFE_Status_t Status, UtAssert_CaseType_t CaseType, const char *File, uint32 Line, const char *Text) { @@ -125,7 +130,7 @@ bool CFE_Assert_Status_DeferredCheck(CFE_Status_t Status, UtAssert_CaseType_t Ca } else { - Result = (Status == CFE_Assert_Global.StoredStatus); + Result = CFE_Assert_Status_SilentCheck(Status); if (Result) { /* no extra tag added to "true" conditions */ diff --git a/modules/cfe_testcase/CMakeLists.txt b/modules/cfe_testcase/CMakeLists.txt index bed726c1a..6c83749ca 100644 --- a/modules/cfe_testcase/CMakeLists.txt +++ b/modules/cfe_testcase/CMakeLists.txt @@ -2,7 +2,7 @@ include_directories(inc) # Filenames based on doxygen groups. # Create the app module -add_cfe_app(cfe_testcase +add_cfe_app(cfe_testcase src/cfe_test.c src/cfe_test_table.c src/es_application_control_test.c @@ -23,6 +23,7 @@ add_cfe_app(cfe_testcase src/message_id_test.c src/msg_api_test.c src/resource_id_misc_test.c + src/sb_performance_test.c src/sb_pipe_mang_test.c src/sb_sendrecv_test.c src/sb_subscription_test.c diff --git a/modules/cfe_testcase/src/cfe_test.c b/modules/cfe_testcase/src/cfe_test.c index 9a655f0ea..388a289bf 100644 --- a/modules/cfe_testcase/src/cfe_test.c +++ b/modules/cfe_testcase/src/cfe_test.c @@ -78,6 +78,7 @@ void CFE_TestMain(void) SBPipeMangSetup(); SBSendRecvTestSetup(); SBSubscriptionTestSetup(); + SBPerformanceTestSetup(); TBLContentAccessTestSetup(); TBLContentMangTestSetup(); TBLInformationTestSetup(); diff --git a/modules/cfe_testcase/src/cfe_test.h b/modules/cfe_testcase/src/cfe_test.h index 1bd1c4e65..c31e83a5a 100644 --- a/modules/cfe_testcase/src/cfe_test.h +++ b/modules/cfe_testcase/src/cfe_test.h @@ -89,6 +89,7 @@ void FSUtilTestSetup(void); void MessageIdTestSetup(void); void MsgApiTestSetup(void); void ResourceIdMiscTestSetup(void); +void SBPerformanceTestSetup(void); void SBPipeMangSetup(void); void SBSendRecvTestSetup(void); void SBSubscriptionTestSetup(void); diff --git a/modules/cfe_testcase/src/sb_performance_test.c b/modules/cfe_testcase/src/sb_performance_test.c new file mode 100644 index 000000000..e56b1e09d --- /dev/null +++ b/modules/cfe_testcase/src/sb_performance_test.c @@ -0,0 +1,147 @@ +/************************************************************************ + * NASA Docket No. GSC-18,719-1, and identified as “core Flight System: Bootes” + * + * Copyright (c) 2020 United States Government as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ************************************************************************/ + +/** + * @file + * + * Functional test of SB transmit/receive API performance + * + * The intent of this test is to perform a set of SB message transfers + * at a sufficiently high rate / volume such that the performance of + * the implementation can be characterized. Note that this + * cannot (currently) measure the performance directly, it merely implements a + * scenario that allows the performance to be measured by an external test + * harness. + */ + +#include "cfe_test.h" +#include "cfe_msgids.h" +#include "cfe_test_msgids.h" + +/* A simple command message */ +typedef struct +{ + CFE_MSG_CommandHeader_t CommandHeader; + uint32 CmdPayload; +} CFE_FT_TestCmdMessage_t; + +/* A simple telemetry message */ +typedef struct +{ + CFE_MSG_TelemetryHeader_t TelemetryHeader; + uint32 TlmPayload; +} CFE_FT_TestTlmMessage_t; + +/* + * This test procedure should be agnostic to specific MID values, but it should + * not overlap/interfere with real MIDs used by other apps. + */ +static const CFE_SB_MsgId_t CFE_FT_CMD_MSGID = CFE_SB_MSGID_WRAP_VALUE(CFE_TEST_CMD_MID); +static const CFE_SB_MsgId_t CFE_FT_TLM_MSGID = CFE_SB_MSGID_WRAP_VALUE(CFE_TEST_HK_TLM_MID); + +void TestBulkTransmitRecv(void) +{ + CFE_SB_PipeId_t PipeId1 = CFE_SB_INVALID_PIPE; + CFE_SB_PipeId_t PipeId2 = CFE_SB_INVALID_PIPE; + CFE_FT_TestCmdMessage_t CmdMsg; + CFE_FT_TestTlmMessage_t TlmMsg; + CFE_SB_Buffer_t * MsgBuf; + const CFE_FT_TestCmdMessage_t *CmdPtr; + const CFE_FT_TestTlmMessage_t *TlmPtr; + uint32 SendCount; + OS_time_t StartTime; + OS_time_t ElapsedTime; + + memset(&CmdMsg, 0, sizeof(CmdMsg)); + memset(&TlmMsg, 0, sizeof(TlmMsg)); + + UtPrintf("Testing: Bulk SB Transmit/Receive"); + CFE_PSP_GetTime(&StartTime); + + /* Setup, create a pipe and subscribe (one cmd, one tlm) */ + UtAssert_INT32_EQ(CFE_SB_CreatePipe(&PipeId1, 5, "TestPipe1"), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_SB_CreatePipe(&PipeId2, 5, "TestPipe2"), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_SB_SubscribeEx(CFE_FT_CMD_MSGID, PipeId1, CFE_SB_DEFAULT_QOS, 3), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_SB_SubscribeEx(CFE_FT_TLM_MSGID, PipeId2, CFE_SB_DEFAULT_QOS, 3), CFE_SUCCESS); + + /* Initialize the message content */ + UtAssert_INT32_EQ(CFE_MSG_Init(CFE_MSG_PTR(CmdMsg.CommandHeader), CFE_FT_CMD_MSGID, sizeof(CmdMsg)), CFE_SUCCESS); + UtAssert_INT32_EQ(CFE_MSG_Init(CFE_MSG_PTR(TlmMsg.TelemetryHeader), CFE_FT_TLM_MSGID, sizeof(TlmMsg)), CFE_SUCCESS); + + for (SendCount = 0; SendCount < 1000000; ++SendCount) + { + CmdMsg.CmdPayload = SendCount; + TlmMsg.TlmPayload = ~SendCount; + + /* In order to not "flood" with test results, this should be silent unless a failure occurs */ + CFE_Assert_STATUS_STORE(CFE_SB_TransmitMsg(CFE_MSG_PTR(CmdMsg.CommandHeader), true)); + if (!CFE_Assert_STATUS_SILENTCHECK(CFE_SUCCESS)) + { + break; + } + + CFE_Assert_STATUS_STORE(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmMsg.TelemetryHeader), true)); + if (!CFE_Assert_STATUS_SILENTCHECK(CFE_SUCCESS)) + { + break; + } + + CFE_Assert_STATUS_STORE(CFE_SB_ReceiveBuffer(&MsgBuf, PipeId1, CFE_SB_POLL)); + if (!CFE_Assert_STATUS_SILENTCHECK(CFE_SUCCESS)) + { + break; + } + + /* As above, to avoid flooding of test cases, only report mismatch here */ + CmdPtr = (const void *)MsgBuf; + if (CmdPtr->CmdPayload != CmdMsg.CmdPayload) + { + UtAssert_UINT32_EQ(CmdPtr->CmdPayload, CmdMsg.CmdPayload); + break; + } + + CFE_Assert_STATUS_STORE(CFE_SB_ReceiveBuffer(&MsgBuf, PipeId2, CFE_SB_POLL)); + if (!CFE_Assert_STATUS_SILENTCHECK(CFE_SUCCESS)) + { + break; + } + + TlmPtr = (const void *)MsgBuf; + if (TlmPtr->TlmPayload != TlmMsg.TlmPayload) + { + UtAssert_UINT32_EQ(TlmPtr->TlmPayload, TlmMsg.TlmPayload); + break; + } + + /* report progress periodically */ + if ((SendCount % 50000) == 0) + { + UtPrintf("Success after %lu messages", (unsigned long)SendCount); + } + } + + CFE_PSP_GetTime(&ElapsedTime); + ElapsedTime = OS_TimeSubtract(ElapsedTime, StartTime); + + UtAssert_MIR("Elapsed time for SB bulk message test: %lu usec", OS_TimeGetTotalMicroseconds(ElapsedTime)); +} + +void SBPerformanceTestSetup(void) +{ + UtTest_Add(TestBulkTransmitRecv, NULL, NULL, "Test Bulk SB Transmit/Receive"); +}