blob: f3582ad6c5f7f018671d432fdbca21f0fa98ec8c [file] [log] [blame]
/*
*
* 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);
}