| /* |
| * |
| * Copyright (c) 2016-2017 Nest Labs, Inc. |
| * 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 |
| * Sample log outputs for testing |
| * |
| */ |
| |
| #ifndef __STDC_LIMIT_MACROS |
| #define __STDC_LIMIT_MACROS |
| #endif |
| |
| #include <new> |
| #include <stdint.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <nlbyteorder.h> |
| #include <nlunit-test.h> |
| |
| // Note that the choice of namespace alias must be made up front for each and every compile unit |
| // This is because many include paths could set the default alias to unintended target. |
| #include <Weave/Profiles/bulk-data-transfer/Development/BDXManagedNamespace.hpp> |
| #include <Weave/Profiles/data-management/Current/WdmManagedNamespace.h> |
| |
| #include "ToolCommon.h" |
| #include <InetLayer/Inet.h> |
| #include <Weave/Core/WeaveCore.h> |
| #include <Weave/Core/WeaveMessageLayer.h> |
| #include <Weave/Core/WeaveEncoding.h> |
| #include <Weave/Core/WeaveTLV.h> |
| #include <Weave/Core/WeaveTLVDebug.hpp> |
| #include <Weave/Core/WeaveTLVUtilities.hpp> |
| #include <Weave/Core/WeaveTLVData.hpp> |
| #include <Weave/Core/WeaveSecurityMgr.h> |
| #include <Weave/Profiles/security/WeaveSecurity.h> |
| #include <Weave/Profiles/ProfileCommon.h> |
| #include <Weave/Profiles/time/WeaveTime.h> |
| #include <Weave/Profiles/data-management/DataManagement.h> |
| #include <Weave/Support/CodeUtils.h> |
| |
| #include <MockEvents.h> |
| |
| using namespace nl::Weave::TLV; |
| using namespace nl::Weave::Profiles::DataManagement; |
| |
| static bool HandleOption(const char * progName, OptionSet * optSet, int id, const char * name, const char * arg); |
| |
| namespace nl { |
| namespace Weave { |
| namespace Profiles { |
| namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current) { |
| namespace Platform { |
| // for unit tests, the dummy critical section is sufficient. |
| void CriticalSectionEnter() |
| { |
| return; |
| } |
| |
| void CriticalSectionExit() |
| { |
| return; |
| } |
| } // namespace Platform |
| } // namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current) |
| } // namespace Profiles |
| } // namespace Weave |
| } // namespace nl |
| |
| nl::Weave::Profiles::DataManagement::SubscriptionEngine * nl::Weave::Profiles::DataManagement::SubscriptionEngine::GetInstance() |
| { |
| static nl::Weave::Profiles::DataManagement::SubscriptionEngine gWdmSubscriptionEngine; |
| |
| return &gWdmSubscriptionEngine; |
| } |
| |
| #define TOOL_NAME "GenerateEventLog" |
| |
| #define LOG_BUFFER_SIZE 512 |
| |
| static const uint64_t kTestNodeId = 0x18B4300001408362ULL; |
| |
| const uint64_t kSubscriptionId = 0xB6C4B7BE2C4B859AULL; |
| |
| enum WDMTags |
| { |
| kCsTag_SubscriptionId = 1, |
| kCsTag_PossibleLossOfEvent = 20, |
| kCsTag_UTCTimestamp = 21, |
| kCsTag_SystemTimestamp = 22, |
| kCsTag_EventList = 23 |
| }; |
| |
| /************************************************************************/ |
| struct LogContext |
| { |
| LogContext(); |
| WeaveExchangeManager * mExchangeMgr; |
| const char * mOutputFilename; |
| size_t mTestNum; |
| nl::Weave::Profiles::DataManagement::ImportanceType mLogLevel; |
| bool mRaw; |
| bool mVerbose; |
| bool mBDX; |
| bool mWDMOutput; |
| }; |
| |
| LogContext gLogContext; |
| |
| LogContext::LogContext() : |
| mExchangeMgr(NULL), mOutputFilename(NULL), mTestNum(0), mLogLevel(nl::Weave::Profiles::DataManagement::Production), mRaw(false), |
| mVerbose(false), mBDX(false), mWDMOutput(false) |
| { } |
| |
| static int TestSetup(void * inContext) |
| { |
| LogContext * ctx = static_cast<LogContext *>(inContext); |
| static WeaveFabricState sFabricState; |
| static WeaveExchangeManager sExchangeMgr; |
| |
| if (ctx->mBDX) |
| { |
| InitSystemLayer(); |
| InitNetwork(); |
| InitWeaveStack(true, true); |
| |
| ctx->mExchangeMgr = &ExchangeMgr; |
| } |
| else |
| { |
| // fake Weave exchange layer. We are not running any |
| // networking tests, and at that point the only functionality |
| // required by the event logging subsystem is that the |
| // ExchageManager has a fabric state with a node id. |
| |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| err = sFabricState.Init(); |
| if (err != WEAVE_NO_ERROR) |
| return FAILURE; |
| |
| sFabricState.LocalNodeId = kTestNodeId; |
| sExchangeMgr.FabricState = &sFabricState; |
| sExchangeMgr.State = WeaveExchangeManager::kState_Initialized; |
| ctx->mExchangeMgr = &sExchangeMgr; |
| } |
| return SUCCESS; |
| } |
| |
| static int TestTeardown(void * inContext) |
| { |
| LogContext * ctx = static_cast<LogContext *>(inContext); |
| if (ctx->mBDX) |
| { |
| ShutdownWeaveStack(); |
| ShutdownNetwork(); |
| ShutdownSystemLayer(); |
| } |
| return SUCCESS; |
| } |
| |
| uint64_t gDebugEventBuffer[LOG_BUFFER_SIZE]; |
| uint64_t gInfoEventBuffer[LOG_BUFFER_SIZE]; |
| uint64_t gProdEventBuffer[LOG_BUFFER_SIZE]; |
| uint64_t gCritEventBuffer[LOG_BUFFER_SIZE]; |
| FILE * gFileOutput = NULL; |
| |
| void InitializeEventLogging(LogContext * context) |
| { |
| LogStorageResources logStorageResources[] = { { static_cast<void *>(&gCritEventBuffer[0]), sizeof(gCritEventBuffer), NULL, 0, |
| NULL, nl::Weave::Profiles::DataManagement::ImportanceType::ProductionCritical }, |
| { static_cast<void *>(&gProdEventBuffer[0]), sizeof(gProdEventBuffer), NULL, 0, |
| NULL, nl::Weave::Profiles::DataManagement::ImportanceType::Production }, |
| { static_cast<void *>(&gInfoEventBuffer[0]), sizeof(gInfoEventBuffer), NULL, 0, |
| NULL, nl::Weave::Profiles::DataManagement::ImportanceType::Info }, |
| { static_cast<void *>(&gDebugEventBuffer[0]), sizeof(gDebugEventBuffer), NULL, 0, |
| NULL, nl::Weave::Profiles::DataManagement::ImportanceType::Debug } }; |
| |
| nl::Weave::Profiles::DataManagement::LoggingManagement::CreateLoggingManagement( |
| context->mExchangeMgr, sizeof(logStorageResources) / sizeof(logStorageResources[0]), logStorageResources); |
| nl::Weave::Profiles::DataManagement::LoggingConfiguration::GetInstance().mGlobalImportance = context->mLogLevel; |
| } |
| |
| void SimpleDumpWriter(const char * aFormat, ...) |
| { |
| va_list args; |
| |
| va_start(args, aFormat); |
| if (gFileOutput) |
| vfprintf(gFileOutput, aFormat, args); |
| else |
| vprintf(aFormat, args); |
| |
| va_end(args); |
| } |
| |
| void DumpEventLog(LogContext * inContext) |
| { |
| uint8_t backingStore[LOG_BUFFER_SIZE * 8]; |
| size_t elementCount; |
| event_id_t eventId = 0; |
| TLVWriter writer; |
| TLVReader reader; |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| FILE * out = stdout; |
| TLVType dummyType; |
| TLVType dummyType1; |
| |
| if (inContext->mOutputFilename) |
| { |
| gFileOutput = fopen(inContext->mOutputFilename, "w"); |
| } |
| if (gFileOutput) |
| { |
| out = gFileOutput; |
| } |
| |
| writer.Init(backingStore, LOG_BUFFER_SIZE * 8); |
| |
| if (inContext->mWDMOutput) |
| { |
| err = writer.StartContainer(AnonymousTag, kTLVType_Structure, dummyType); |
| SuccessOrExit(err); |
| |
| err = writer.Put(ContextTag(kCsTag_SubscriptionId), kSubscriptionId); |
| SuccessOrExit(err); |
| |
| err = writer.StartContainer(ContextTag(kCsTag_EventList), kTLVType_Array, dummyType1); |
| SuccessOrExit(err); |
| } |
| |
| err = nl::Weave::Profiles::DataManagement::LoggingManagement::GetInstance().FetchEventsSince( |
| writer, nl::Weave::Profiles::DataManagement::Production, eventId); |
| if ((err == WEAVE_END_OF_TLV) || (err == WEAVE_ERROR_TLV_UNDERRUN)) |
| { |
| err = WEAVE_NO_ERROR; |
| } |
| SuccessOrExit(err); |
| eventId = 0; |
| err = nl::Weave::Profiles::DataManagement::LoggingManagement::GetInstance().FetchEventsSince( |
| writer, nl::Weave::Profiles::DataManagement::Info, eventId); |
| if ((err == WEAVE_END_OF_TLV) || (err == WEAVE_ERROR_TLV_UNDERRUN)) |
| { |
| err = WEAVE_NO_ERROR; |
| } |
| SuccessOrExit(err); |
| eventId = 0; |
| err = nl::Weave::Profiles::DataManagement::LoggingManagement::GetInstance().FetchEventsSince( |
| writer, nl::Weave::Profiles::DataManagement::Debug, eventId); |
| if ((err == WEAVE_END_OF_TLV) || (err == WEAVE_ERROR_TLV_UNDERRUN)) |
| { |
| err = WEAVE_NO_ERROR; |
| } |
| SuccessOrExit(err); |
| eventId = 0; |
| err = writer.Finalize(); |
| SuccessOrExit(err); |
| |
| if (inContext->mWDMOutput) |
| { |
| err = writer.EndContainer(kTLVType_Array); |
| SuccessOrExit(err); |
| |
| err = writer.EndContainer(kTLVType_Structure); |
| SuccessOrExit(err); |
| } |
| |
| if (inContext->mRaw) |
| { |
| fwrite(backingStore, 1, writer.GetLengthWritten(), out); |
| } |
| else |
| { |
| reader.Init(backingStore, writer.GetLengthWritten()); |
| fprintf(out, "Wrote %d bytes to the log\n", writer.GetLengthWritten()); |
| nl::Weave::TLV::Utilities::Count(reader, elementCount); |
| fprintf(out, "Fetched %lu elements, last eventID: %u \n", elementCount, eventId); |
| nl::Weave::TLV::Debug::Dump(reader, SimpleDumpWriter); |
| } |
| exit: |
| if (err != WEAVE_NO_ERROR) |
| { |
| fprintf(stderr, "Error occurred: %d\n", err); |
| } |
| return; |
| } |
| |
| void SimpleDebugLog(void * inContext) |
| { |
| DebugEventGenerator generator; |
| for (size_t i = 0; i < generator.GetNumStates(); i++) |
| { |
| generator.Generate(); |
| usleep(10000); |
| } |
| return; |
| } |
| |
| void SimpleHeartbeatLog(void * inContext) |
| { |
| LivenessEventGenerator generator; |
| for (size_t i = 0; i < generator.GetNumStates(); i++) |
| { |
| generator.Generate(); |
| usleep(10000); |
| } |
| return; |
| } |
| |
| void SimpleSecurityLog(void * inContext) |
| { |
| |
| useconds_t delays[] = { |
| 10000, 10000, 10000, 5000, 5000, 10000, 100000, 10000, 10000, 10000, 10000, 5000, 1000, 1000, 1000, 1000 |
| }; |
| SecurityEventGenerator generator; |
| |
| for (size_t i = 0; i < generator.GetNumStates(); i++) |
| { |
| generator.Generate(); |
| usleep(delays[i]); |
| } |
| return; |
| } |
| |
| typedef void (*LogGenerator)(void *); |
| const LogGenerator gTests[] = { SimpleDebugLog, SimpleHeartbeatLog, SimpleSecurityLog }; |
| |
| const size_t gNumTests = sizeof(gTests) / sizeof(void (*)(void *)); |
| |
| static OptionDef gToolOptionDefs[] = { { "loglevel", kArgumentRequired, 'l' }, |
| { "output", kArgumentRequired, 'o' }, |
| { "raw", kNoArgument, 'r' }, |
| { "test", kArgumentRequired, 't' }, |
| { "verbose", kNoArgument, 'V' }, |
| { "wdm", kNoArgument, 'w' }, |
| { } }; |
| |
| static const char * gToolOptionHelp = " -l, --loglevel <logLevel>\n" |
| " Configured default log level, 1 - PRODUCTION, 2 - INFO, 3 - DEBUG\n" |
| " -o, --output <filename>\n" |
| " Save the output in the file\n" |
| " -r, --raw\n" |
| " Emit raw bytes\n" |
| " -t, --test <num>\n" |
| " The test log to use, valid range: 1 to %d\n" |
| " -V, --verbose\n" |
| " Verbose output\n" |
| " -w, --wdm\n" |
| " Enclose the output in the WDM Notification envelope\n"; |
| |
| static OptionSet gToolOptions = { HandleOption, gToolOptionDefs, "GENERAL OPTIONS", gToolOptionHelp }; |
| |
| static HelpOptions gHelpOptions(TOOL_NAME, "Usage: " TOOL_NAME " [<options...>]\n", WEAVE_VERSION_STRING "\n" WEAVE_TOOL_COPYRIGHT, |
| "Generate a sample event log showing various features of the event encoding.\n"); |
| |
| static OptionSet * gToolOptionSets[] = { &gToolOptions, &gFaultInjectionOptions, &gHelpOptions, NULL }; |
| |
| bool HandleOption(const char * progName, OptionSet * optSet, int id, const char * name, const char * arg) |
| { |
| uint32_t level, testNum; |
| |
| switch (id) |
| { |
| case 'l': |
| if (!ParseInt(arg, level) || level == 0 || level > 3) |
| { |
| PrintArgError("%s: Invalid value specified for logging level: %s\n", progName, arg); |
| return false; |
| } |
| gLogContext.mLogLevel = static_cast<nl::Weave::Profiles::DataManagement::ImportanceType>(level); |
| break; |
| |
| case 'o': |
| gLogContext.mOutputFilename = arg; |
| break; |
| |
| case 'r': |
| gLogContext.mRaw = true; |
| break; |
| |
| case 't': |
| if (!ParseInt(arg, testNum) || testNum == 0 || testNum > gNumTests) |
| { |
| PrintArgError("%s: Invalid value specified for test number: %s\n", progName, arg); |
| return false; |
| } |
| gLogContext.mTestNum = testNum - 1; |
| break; |
| |
| case 'V': |
| gLogContext.mVerbose = true; |
| break; |
| |
| case 'w': |
| gLogContext.mWDMOutput = true; |
| break; |
| |
| default: |
| PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", progName, name); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| int main(int argc, char * argv[]) |
| { |
| int status = 0; |
| |
| status = asprintf((char **) &gToolOptions.OptionHelp, gToolOptionHelp, gNumTests); |
| VerifyOrExit(status >= 0, /* no-op */); |
| |
| if (!ParseArgsFromEnvVar(TOOL_NAME, TOOL_OPTIONS_ENV_VAR_NAME, gToolOptionSets, NULL, true) || |
| !ParseArgs(TOOL_NAME, argc, argv, gToolOptionSets)) |
| { |
| exit(EXIT_FAILURE); |
| } |
| |
| TestSetup(&gLogContext); |
| |
| InitializeEventLogging(&gLogContext); |
| |
| gTests[gLogContext.mTestNum](&gLogContext); |
| |
| DumpEventLog(&gLogContext); |
| |
| TestTeardown(&gLogContext); |
| |
| exit: |
| return ((status >= 0) ? EXIT_SUCCESS : EXIT_FAILURE); |
| } |