blob: 411ef17f31598c9cfe54589ec7c028e1a509d8ae [file] [log] [blame]
/*
*
* Copyright (c) 2015-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.
*/
// __STDC_FORMAT_MACROS must be defined for PRIX64 to be defined for pre-C++11 clib
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif // __STDC_FORMAT_MACROS
// it is important for this first inclusion of inttypes.h to have all the right switches turned ON
#include <inttypes.h>
#define WEAVE_CONFIG_BDX_NAMESPACE kWeaveManagedNamespace_Development
#include <Weave/Profiles/data-management/Current/WdmManagedNamespace.h>
#include <Weave/Profiles/data-management/DataManagement.h>
#include <Weave/Profiles/WeaveProfiles.h>
#include <Weave/Profiles/bulk-data-transfer/Development/BulkDataTransfer.h>
#include <Weave/Profiles/bulk-data-transfer/Development/BDXMessages.h>
#include <SystemLayer/SystemTimer.h>
#if HAVE_NEW
#include <new>
#else
inline void * operator new(size_t, void * p) throw()
{
return p;
}
inline void * operator new[](size_t, void * p) throw()
{
return p;
}
#endif // HAVE_NEW
using namespace nl::Weave::TLV;
namespace nl {
namespace Weave {
namespace Profiles {
namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current) {
// Events are embedded in an anonymous structure: 1 for the control byte, 1 for end-of-container
#define EVENT_CONTAINER_OVERHEAD_TLV_SIZE 2
// Event importance element consumes 3 bytes: control byte, 1-byte tag, and 1 byte value
#define IMPORTANCE_TLV_SIZE 3
// Overhead of embedding something in a (short) byte string: 1 byte control, 1 byte tag, 1 byte length
#define EXTERNAL_EVENT_BYTE_STRING_TLV_SIZE 3
// Static instance: embedded platforms not always implement a proper
// C++ runtime; instead, the instance is initialized via placement new
// in CreateLoggingManagement.
static LoggingManagement sInstance;
LoggingManagement & LoggingManagement::GetInstance(void)
{
return sInstance;
}
struct ReclaimEventCtx
{
CircularEventBuffer * mEventBuffer;
size_t mSpaceNeededForEvent;
};
WEAVE_ERROR LoggingManagement::AlwaysFail(nl::Weave::TLV::WeaveCircularTLVBuffer & inBuffer, void * inAppData,
nl::Weave::TLV::TLVReader & inReader)
{
return WEAVE_ERROR_NO_MEMORY;
}
WEAVE_ERROR LoggingManagement::CopyToNextBuffer(CircularEventBuffer * inEventBuffer)
{
CircularTLVWriter writer;
CircularTLVReader reader;
WeaveCircularTLVBuffer checkpoint = inEventBuffer->mNext->mBuffer;
WeaveCircularTLVBuffer * nextBuffer = &(inEventBuffer->mNext->mBuffer);
WEAVE_ERROR err;
// Set up the next buffer s.t. it fails if needs to evict an element
nextBuffer->mProcessEvictedElement = AlwaysFail;
writer.Init(nextBuffer);
// Set up the reader s.t. it is positioned to read the head event
reader.Init(&(inEventBuffer->mBuffer));
err = reader.Next();
SuccessOrExit(err);
err = writer.CopyElement(reader);
SuccessOrExit(err);
err = writer.Finalize();
SuccessOrExit(err);
exit:
if (err != WEAVE_NO_ERROR)
{
inEventBuffer->mNext->mBuffer = checkpoint;
}
return err;
}
WEAVE_ERROR LoggingManagement::EnsureSpace(size_t inRequiredSpace)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
size_t requiredSpace = inRequiredSpace;
CircularEventBuffer * eventBuffer = mEventBuffer;
WeaveCircularTLVBuffer * circularBuffer;
ReclaimEventCtx ctx;
// check whether we actually need to do anything, exit if we don't
VerifyOrExit(requiredSpace > eventBuffer->mBuffer.AvailableDataLength(), err = WEAVE_NO_ERROR);
while (true)
{
circularBuffer = &(eventBuffer->mBuffer);
// check that the request can ultimately be satisfied.
VerifyOrExit(requiredSpace <= circularBuffer->GetQueueSize(), err = WEAVE_ERROR_BUFFER_TOO_SMALL);
if (requiredSpace > circularBuffer->AvailableDataLength())
{
ctx.mEventBuffer = eventBuffer;
ctx.mSpaceNeededForEvent = 0;
circularBuffer->mProcessEvictedElement = EvictEvent;
circularBuffer->mAppData = &ctx;
err = circularBuffer->EvictHead();
// one of two things happened: either the element was evicted,
// or we figured out how much space we need to evict it into
// the next buffer
if (err != WEAVE_NO_ERROR)
{
VerifyOrExit(ctx.mSpaceNeededForEvent != 0, /* no-op, return err */);
if (ctx.mSpaceNeededForEvent <= eventBuffer->mNext->mBuffer.AvailableDataLength())
{
// we can copy the event outright. copy event and
// subsequently evict head s.t. evicting the head
// element always succeeds.
// Since we're calling CopyElement and we've checked
// that there is space in the next buffer, we don't expect
// this to fail.
err = CopyToNextBuffer(eventBuffer);
SuccessOrExit(err);
// success; evict head unconditionally
circularBuffer->mProcessEvictedElement = NULL;
err = circularBuffer->EvictHead();
// if unconditional eviction failed, this
// means that we have no way of further
// clearing the buffer. fail out and let the
// caller know that we could not honor the
// request
SuccessOrExit(err);
continue;
}
// we cannot copy event outright. We remember the
// current required space in mAppData, we note the
// space requirements for the event in the current
// buffer and make that space in the next buffer.
circularBuffer->mAppData = reinterpret_cast<void *>(requiredSpace);
eventBuffer = eventBuffer->mNext;
// Sanity check: Die here on null event buffer. If
// eventBuffer->mNext were null, then the `EvictBuffer`
// in line 130 would have succeeded -- the event was
// already in the final buffer.
VerifyOrDie(eventBuffer != NULL);
requiredSpace = ctx.mSpaceNeededForEvent;
}
}
else
{
if (eventBuffer == mEventBuffer)
break;
eventBuffer = eventBuffer->mPrev;
requiredSpace = reinterpret_cast<size_t>(eventBuffer->mBuffer.mAppData);
err = WEAVE_NO_ERROR;
}
}
// On exit, configure the top-level s.t. it will always fail to evict an element
mEventBuffer->mBuffer.mProcessEvictedElement = AlwaysFail;
mEventBuffer->mBuffer.mAppData = NULL;
exit:
return err;
}
/**
* @brief Helper function for writing event header and data according to event
* logging protocol.
*
* @param[inout] aContext EventLoadOutContext, initialized with stateful
* information for the buffer. State is updated
* and preserved by BlitEvent using this context.
*
* @param[in] inSchema Schema defining importance, profile ID, and
* structure type of this event.
*
* @param[in] inEventWriter The callback to invoke to serialize the event data.
*
* @param[in] inAppData Application context for the callback.
*
* @param[in] inOptions EventOptions describing timestamp and other tags
* relevant to this event.
*
*/
WEAVE_ERROR LoggingManagement::BlitEvent(EventLoadOutContext * aContext, const EventSchema & inSchema,
EventWriterFunct inEventWriter, void * inAppData, const EventOptions * inOptions)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
TLVWriter checkpoint = aContext->mWriter;
TLVType containerType;
VerifyOrExit(aContext->mCurrentEventID >= aContext->mStartingEventID,
/* no-op: don't write event, but advance current event ID */);
VerifyOrExit(inOptions != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
VerifyOrExit(inOptions->timestampType != kTimestampType_Invalid, err = WEAVE_ERROR_INVALID_ARGUMENT);
err = aContext->mWriter.StartContainer(AnonymousTag, kTLVType_Structure, containerType);
SuccessOrExit(err);
// Event metadata
// Importance
err = aContext->mWriter.Put(ContextTag(kTag_EventImportance), static_cast<uint16_t>(inSchema.mImportance));
SuccessOrExit(err);
// If mFirst, record event ID
if (aContext->mFirst)
{
err = aContext->mWriter.Put(ContextTag(kTag_EventID), aContext->mCurrentEventID);
SuccessOrExit(err);
}
// Related Event processing
if (inOptions->relatedEventID != 0)
{
err = aContext->mWriter.Put(ContextTag(kTag_RelatedEventImportance), static_cast<uint16_t>(inOptions->relatedImportance));
SuccessOrExit(err);
err = aContext->mWriter.Put(ContextTag(kTag_RelatedEventID), inOptions->relatedEventID);
SuccessOrExit(err);
}
// if mFirst, record absolute time
if (aContext->mFirst)
{
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
if (inOptions->timestampType == kTimestampType_UTC)
{
err = aContext->mWriter.Put(ContextTag(kTag_EventUTCTimestamp), inOptions->timestamp.utcTimestamp);
SuccessOrExit(err);
}
else
#endif // WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
{
err = aContext->mWriter.Put(ContextTag(kTag_EventSystemTimestamp), inOptions->timestamp.systemTimestamp);
SuccessOrExit(err);
}
}
// else record delta
else
{
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
if (inOptions->timestampType == kTimestampType_UTC)
{
int64_t deltatime = inOptions->timestamp.utcTimestamp - aContext->mCurrentUTCTime;
err = aContext->mWriter.Put(ContextTag(kTag_EventDeltaUTCTime), deltatime);
SuccessOrExit(err);
}
else
#endif // WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
{
int32_t deltatime = inOptions->timestamp.systemTimestamp - aContext->mCurrentTime;
err = aContext->mWriter.Put(ContextTag(kTag_EventDeltaSystemTime), deltatime);
SuccessOrExit(err);
}
}
// Event Trait Profile ID
if (inSchema.mMinCompatibleDataSchemaVersion != 1 || inSchema.mDataSchemaVersion != 1)
{
TLV::TLVType type;
err = aContext->mWriter.StartContainer(ContextTag(kTag_EventTraitProfileID), kTLVType_Array, type);
SuccessOrExit(err);
err = aContext->mWriter.Put(TLV::AnonymousTag, inSchema.mProfileId);
SuccessOrExit(err);
if (inSchema.mDataSchemaVersion != 1)
{
err = aContext->mWriter.Put(TLV::AnonymousTag, inSchema.mDataSchemaVersion);
SuccessOrExit(err);
}
if (inSchema.mMinCompatibleDataSchemaVersion != 1)
{
err = aContext->mWriter.Put(TLV::AnonymousTag, inSchema.mMinCompatibleDataSchemaVersion);
SuccessOrExit(err);
}
err = aContext->mWriter.EndContainer(type);
SuccessOrExit(err);
}
else
{
err = aContext->mWriter.Put(ContextTag(kTag_EventTraitProfileID), inSchema.mProfileId);
SuccessOrExit(err);
}
// Event resource
if (inOptions->eventSource != NULL)
{
err = inOptions->eventSource->ResourceID.ToTLV(aContext->mWriter, ContextTag(kTag_EventResourceID));
SuccessOrExit(err);
err = aContext->mWriter.Put(ContextTag(kTag_EventTraitInstanceID), inOptions->eventSource->TraitInstanceID);
SuccessOrExit(err);
}
// Event Type (aka Event Message ID)
err = aContext->mWriter.Put(ContextTag(kTag_EventType), inSchema.mStructureType);
SuccessOrExit(err);
// Callback to write the EventData
err = inEventWriter(aContext->mWriter, kTag_EventData, inAppData);
SuccessOrExit(err);
err = aContext->mWriter.EndContainer(containerType);
SuccessOrExit(err);
err = aContext->mWriter.Finalize();
SuccessOrExit(err);
// only update mFirst if an event was successfully written.
if (aContext->mFirst)
{
aContext->mFirst = false;
}
exit:
if (err != WEAVE_NO_ERROR)
{
aContext->mWriter = checkpoint;
}
else
{
// update these variables since BlitEvent can be used to track the
// state of a set of events over multiple calls.
aContext->mCurrentEventID++;
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
if (inOptions->timestampType == kTimestampType_UTC)
{
aContext->mCurrentUTCTime = inOptions->timestamp.utcTimestamp;
}
else
#endif // WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
{
aContext->mCurrentTime = inOptions->timestamp.systemTimestamp;
}
}
return err;
}
/**
* @brief Helper function to skip writing an event corresponding to an allocated
* event id.
*
* @param[inout] aContext EventLoadOutContext, initialized with stateful
* information for the buffer. State is updated
* and preserved by BlitEvent using this context.
*
*/
void LoggingManagement::SkipEvent(EventLoadOutContext * aContext)
{
aContext->mCurrentEventID++; // Advance the event id without writing anything
}
/**
* @brief Create LoggingManagement object and initialize the logging management
* subsystem with provided resources.
*
* Initialize the LoggingManagement with an array of LogStorageResources. The
* array must provide a resource for each valid importance level, the elements
* of the array must be in increasing numerical value of importance (and in
* decreasing importance); the first element in the array corresponds to the
* resources allocated for the most critical events, and the last element
* corresponds to the least important events.
*
* @param[in] inMgr WeaveExchangeManager to be used with this logging subsystem
*
* @param[in] inNumBuffers Number of elements in inLogStorageResources array
*
* @param[in] inLogStorageResources An array of LogStorageResources for each importance level.
*
* @note This function must be called prior to the logging being used.
*/
void LoggingManagement::CreateLoggingManagement(nl::Weave::WeaveExchangeManager * inMgr,
size_t inNumBuffers,
const LogStorageResources * const inLogStorageResources)
{
new (&sInstance) LoggingManagement(inMgr, inNumBuffers, inLogStorageResources);
}
/**
* @brief Perform any actions we need to on shutdown.
*/
void LoggingManagement::DestroyLoggingManagement(void)
{
Platform::CriticalSectionEnter();
sInstance.mState = kLoggingManagementState_Shutdown;
sInstance.mEventBuffer = NULL;
Platform::CriticalSectionExit();
}
/**
* Serialize the Weave events of all importance types.
*
* Serializes the events in WeaveCircularTLVBuffer and the associated states into the
* supplied buffer.
*
* This method is intended to be used by devices that do not retain RAM while sleeping,
* allowing them to persist events before going to sleep and thereby prevent lost of
* events
*/
WEAVE_ERROR LoggingManagement::SerializeEvents(TLVWriter & writer)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Platform::CriticalSectionEnter();
CircularEventBuffer * eventBuffer = mEventBuffer;
TLVType container;
err = writer.StartContainer(ProfileTag(kWeaveProfile_Common, kTagNum_SerializedEventState), kTLVType_Array, container);
SuccessOrExit(err);
while (eventBuffer != NULL)
{
err = eventBuffer->SerializeEvents(writer);
SuccessOrExit(err);
eventBuffer = eventBuffer->mNext;
}
err = writer.EndContainer(container);
SuccessOrExit(err);
exit:
Platform::CriticalSectionExit();
return err;
}
/**
* @brief Load previously persisted Weave event.
*
*/
WEAVE_ERROR LoggingManagement::LoadEvents(TLVReader & reader)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Platform::CriticalSectionEnter();
CircularEventBuffer * eventBuffer = mEventBuffer;
TLVType container;
err = reader.Next(kTLVType_Array, ProfileTag(kWeaveProfile_Common, kTagNum_SerializedEventState));
SuccessOrExit(err);
err = reader.EnterContainer(container);
SuccessOrExit(err);
while (eventBuffer != NULL)
{
err = eventBuffer->LoadEvents(reader);
SuccessOrExit(err);
eventBuffer = eventBuffer->mNext;
}
err = reader.VerifyEndOfContainer();
SuccessOrExit(err);
err = reader.ExitContainer(container);
SuccessOrExit(err);
exit:
Platform::CriticalSectionExit();
return err;
}
/**
* @brief Set mShutdownInProgress flag to true.
*/
void LoggingManagement::MarkShutdownInProgress(void)
{
mState = kLoggingManagementState_Shutdown;
}
/**
* @brief Set mShutdownInProgress flag to false.
*/
void LoggingManagement::CancelShutdownInProgress(void)
{
mState = kLoggingManagementState_Idle;
}
/**
* @brief Check mShutdownInProgress flag.
*/
bool LoggingManagement::IsShutdownInProgress(void)
{
return mState == kLoggingManagementState_Shutdown;
}
/**
* @brief Set the WeaveExchangeManager to be used with this logging subsystem. On some
* platforms, this may need to happen separately from CreateLoggingManagement() above.
*
* @param[in] inMgr WeaveExchangeManager to be used with this logging subsystem
*/
WEAVE_ERROR LoggingManagement::SetExchangeManager(nl::Weave::WeaveExchangeManager * inMgr)
{
mExchangeMgr = inMgr;
return WEAVE_NO_ERROR;
}
/**
* @brief
* LoggingManagement constructor
*
* Initialize the LoggingManagement with an array of LogStorageResources. The
* array must provide a resource for each valid importance level, the elements
* of the array must be in increasing numerical value of importance (and in
* decreasing importance); the first element in the array corresponds to the
* resources allocated for the most critical events, and the last element
* corresponds to the least important events.
*
* @param[in] inMgr WeaveExchangeManager to be used with this logging subsystem
*
* @param[in] inNumBuffers Number of elements in inLogStorageResources array
*
* @param[in] inLogStorageResources An array of LogStorageResources for each importance level.
*
*/
LoggingManagement::LoggingManagement(nl::Weave::WeaveExchangeManager * inMgr,
size_t inNumBuffers,
const LogStorageResources * const inLogStorageResources)
{
CircularEventBuffer * current = NULL;
CircularEventBuffer * prev = NULL;
CircularEventBuffer * next = NULL;
size_t i, j;
VerifyOrDie(inNumBuffers > 0);
mThrottled = 0;
mExchangeMgr = inMgr;
for (j = 0; j < inNumBuffers; j++)
{
i = inNumBuffers - 1 - j;
next = (i > 0) ? static_cast<CircularEventBuffer *>(inLogStorageResources[i - 1].mBuffer) : NULL;
VerifyOrDie(inLogStorageResources[i].mBufferSize > sizeof(CircularEventBuffer));
new (inLogStorageResources[i].mBuffer)
CircularEventBuffer(static_cast<uint8_t *>(inLogStorageResources[i].mBuffer) + sizeof(CircularEventBuffer),
inLogStorageResources[i].mBufferSize - sizeof(CircularEventBuffer), prev, next);
current = prev = static_cast<CircularEventBuffer *>(inLogStorageResources[i].mBuffer);
current->mBuffer.mProcessEvictedElement = AlwaysFail;
current->mBuffer.mAppData = NULL;
current->mImportance = inLogStorageResources[i].mImportance;
if ((inLogStorageResources[i].mCounterStorage != NULL) && (inLogStorageResources[i].mCounterKey != NULL) &&
(inLogStorageResources[i].mCounterEpoch != 0))
{
// We have been provided storage for a counter for this importance level.
new (inLogStorageResources[i].mCounterStorage) PersistedCounter();
WEAVE_ERROR err = inLogStorageResources[i].mCounterStorage->Init(*(inLogStorageResources[i].mCounterKey),
inLogStorageResources[i].mCounterEpoch);
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(EventLogging, "%s PersistedCounter[%d]->Init() failed with %d", __FUNCTION__, j, err);
}
current->mEventIdCounter = inLogStorageResources[i].mCounterStorage;
}
else
{
// No counter has been provided, so we'll use our
// "built-in" non-persisted counter.
current->mNonPersistedCounter.Init(1);
current->mEventIdCounter = &(current->mNonPersistedCounter);
}
current->mFirstEventID = current->mEventIdCounter->GetValue();
}
mEventBuffer = static_cast<CircularEventBuffer *>(inLogStorageResources[kImportanceType_Last - kImportanceType_First].mBuffer);
mState = kLoggingManagementState_Idle;
mBDXUploader = NULL;
mBytesWritten = 0;
mUploadRequested = false;
mMaxImportanceBuffer = kImportanceType_Last;
}
/**
* @brief
* LoggingManagement default constructor. Provided primarily to make the compiler happy.
*
* @return LoggingManagement
*/
LoggingManagement::LoggingManagement(void) :
mEventBuffer(NULL), mExchangeMgr(NULL), mState(kLoggingManagementState_Idle), mBDXUploader(NULL), mBytesWritten(0),
mThrottled(0), mMaxImportanceBuffer(kImportanceType_Invalid), mUploadRequested(false)
{ }
/**
* @brief
* A function to get the current importance of a profile.
*
* The function returns the current importance of a profile as
* currently configured in the #LoggingConfiguration trait. When
* per-profile importance is supported, it is used; otherwise only
* global importance is supported. When the log is throttled, we only
* record the Production events
*
* @param profileId Profile against which the event is being logged
*
* @return Importance of the current profile based on the config
*/
ImportanceType LoggingManagement::GetCurrentImportance(uint32_t profileId)
{
const LoggingConfiguration & config = LoggingConfiguration::GetInstance();
ImportanceType retval;
if (mThrottled != 0)
{
retval = Production;
}
else if (config.SupportsPerProfileImportance())
{
retval = config.GetProfileImportance(profileId);
}
else
{
retval = config.mGlobalImportance;
}
return (retval < mMaxImportanceBuffer ? retval : mMaxImportanceBuffer);
}
/**
* @brief
* Get the max available importance of the system.
*
* This function returns the max importance stored by logging management,
* as defined by both the global importance and number of buffers available
*
* @return ImportanceType Max importance type currently stored.
*/
ImportanceType LoggingManagement::GetMaxImportance(void)
{
const LoggingConfiguration & config = LoggingConfiguration::GetInstance();
return (config.mGlobalImportance < mMaxImportanceBuffer ? config.mGlobalImportance : mMaxImportanceBuffer);
}
/**
* @brief
* Allocate a new event ID based on the event importance, and advance the counter
* if we have one.
*
* @return event_id_t Event ID for this importance.
*/
event_id_t CircularEventBuffer::VendEventID(void)
{
event_id_t retval = 0;
WEAVE_ERROR err = WEAVE_NO_ERROR;
// Assign event ID to the buffer's counter's value.
retval = mEventIdCounter->GetValue();
mLastEventID = static_cast<event_id_t>(retval);
// Now advance the counter.
err = mEventIdCounter->Advance();
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(EventLogging, "%s Advance() for importance %d failed with %d", __FUNCTION__, mImportance, err);
}
return retval;
}
/**
* @brief
* Fetch the most recently vended ID for a particular importance level
*
* @param inImportance Importance level
*
* @return event_id_t most recently vended event ID for that event importance
*/
event_id_t LoggingManagement::GetLastEventID(ImportanceType inImportance)
{
return GetImportanceBuffer(inImportance)->mLastEventID;
}
/**
* @brief
* Fetch the first event ID currently stored for a particular importance level
*
* @param inImportance Importance level
*
* @return event_id_t First currently stored event ID for that event importance
*/
event_id_t LoggingManagement::GetFirstEventID(ImportanceType inImportance)
{
return GetImportanceBuffer(inImportance)->mFirstEventID;
}
CircularEventBuffer * LoggingManagement::GetImportanceBuffer(ImportanceType inImportance) const
{
CircularEventBuffer * buf = mEventBuffer;
while (!buf->IsFinalDestinationForImportance(inImportance))
{
buf = buf->mNext;
}
return buf;
}
#if WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
/**
* @brief
* The public API for registering a set of externally stored events.
*
* Register a callback of form #FetchExternalEventsFunct. This API requires
* the platform to know the number of events on registration. The internal
* workings also require this number to be constant. Since this API
* does not allow the platform to register specific event IDs, this prevents
* the platform from persisting storage of events (at least with unique event
* IDs).
*
* The callback will be called whenever a subscriber attempts to fetch event IDs
* within the range any number of times until it is unregistered.
*
* This variant of the function should be used when the external
* provider desires a notifification neither when the external events have
* been delivered nor when the external events object is evicted.
*
* The pointer to the ExternalEvents struct will be NULL on failure, otherwise
* will be populated with start and end event IDs assigned to the callback.
* This pointer should be used to unregister the set of events.
*
* See the documentation for #FetchExternalEventsFunct for details on what
* the callback must implement.
*
* @param[in] inImportance Importance level
*
* @param[in] inCallback Callback to register to fetch external events
*
* @param[in] inNumEvents Number of events in this set
*
* @param[out] outLastEventID Pointer to an event_id_t; on successful registration of external events the function will store the
* event ID corresponding to the last event ID of the external event block. The parameter may be NULL.
*
* @retval WEAVE_ERROR_NO_MEMORY If no more callback slots are available.
* @retval WEAVE_ERROR_INVALID_ARGUMENT Null function callback or no events to register.
* @retval WEAVE_NO_ERROR On success.
*/
WEAVE_ERROR LoggingManagement::RegisterEventCallbackForImportance(ImportanceType inImportance, FetchExternalEventsFunct inCallback,
size_t inNumEvents, event_id_t * outLastEventID)
{
return RegisterEventCallbackForImportance(inImportance, inCallback, NULL, NULL, inNumEvents, outLastEventID);
}
/**
* @brief
* The public API for registering a set of externally stored events.
*
* Register a callback of form #FetchExternalEventsFunct. This API requires
* the platform to know the number of events on registration. The internal
* workings also require this number to be constant. Since this API
* does not allow the platform to register specific event IDs, this prevents
* the platform from persisting storage of events (at least with unique event
* IDs).
*
* The callback will be called whenever a subscriber attempts to fetch event IDs
* within the range any number of times until it is unregistered.
*
* This variant of the function should be used when the external
* provider wants to be notified when the events have been delivered
* to a subscriber, but not when the external events object is evicted.
* When the events are delivered, the external provider is notified
* about that along with the node ID of the recipient and the id of the
* last event delivered to that recipient. Note that the external
* provider may be notified several times for the same event ID.
* There are no specific restrictions on the handler, in particular,
* the handler may unregister the external event IDs.
*
* The pointer to the ExternalEvents struct will be NULL on failure, otherwise
* will be populated with start and end event IDs assigned to the callback.
* This pointer should be used to unregister the set of events.
*
* See the documentation for #FetchExternalEventsFunct for details on what
* the callback must implement.
*
* @param[in] inImportance Importance level
*
* @param[in] inCallback Callback to register to fetch external events
*
* @param[in] inNotifyCallback Callback to register for delivery notification
*
* @param[in] inNumEvents Number of events in this set
*
* @param[out] outLastEventID Pointer to an event_id_t; on successful registration of external events the function will store the
* event ID corresponding to the last event ID of the external event block. The parameter may be NULL.
*
* @retval WEAVE_ERROR_NO_MEMORY If no more callback slots are available.
* @retval WEAVE_ERROR_INVALID_ARGUMENT Null function callback or no events to register.
* @retval WEAVE_NO_ERROR On success.
*/
WEAVE_ERROR LoggingManagement::RegisterEventCallbackForImportance(ImportanceType inImportance,
FetchExternalEventsFunct inFetchCallback,
NotifyExternalEventsDeliveredFunct inNotifyCallback,
size_t inNumEvents, event_id_t * outLastEventID)
{
return RegisterEventCallbackForImportance(inImportance, inFetchCallback, inNotifyCallback, NULL, inNumEvents, outLastEventID);
}
/**
* @brief
* The public API for registering a set of externally stored events.
*
* Register a callback of form #FetchExternalEventsFunct. This API requires
* the platform to know the number of events on registration. The internal
* workings also require this number to be constant. Since this API
* does not allow the platform to register specific event IDs, this prevents
* the platform from persisting storage of events (at least with unique event
* IDs).
*
* The callback will be called whenever a subscriber attempts to fetch event IDs
* within the range any number of times until it is unregistered.
*
* This variant of the function should be used when the external
* provider wants to be notified both when the events have been delivered
* to a subscriber and if the external events object is evicted.
*
* When the events are delivered, the external provider is notified
* about that along with the node ID of the recipient and the id of the
* last event delivered to that recipient. Note that the external
* provider may be notified several times for the same event ID.
* There are no specific restrictions on the handler, in particular,
* the handler may unregister the external event IDs.
*
* If the external events object is evicted from the log buffers,
* the external provider is notified along with a copy of the external
* events object.
*
* The pointer to the ExternalEvents struct will be NULL on failure, otherwise
* will be populated with start and end event IDs assigned to the callback.
* This pointer should be used to unregister the set of events.
*
* See the documentation for #FetchExternalEventsFunct for details on what
* the callback must implement.
*
* @param[in] inImportance Importance level
*
* @param[in] inFetchCallback Callback to register to fetch external events
*
* @param[in] inNotifyCallback Callback to register for delivery notification
*
* @param[in] inEvictedCallback Callback to register for eviction notification
*
* @param[in] inNumEvents Number of events in this set
*
* @param[out] outLastEventID Pointer to an event_id_t; on successful registration of external events the function will store the
* event ID corresponding to the last event ID of the external event block. The parameter may be NULL.
*
* @retval WEAVE_ERROR_NO_MEMORY If no more callback slots are available.
* @retval WEAVE_ERROR_INVALID_ARGUMENT Null function callback or no events to register.
* @retval WEAVE_NO_ERROR On success.
*/
WEAVE_ERROR LoggingManagement::RegisterEventCallbackForImportance(ImportanceType inImportance,
FetchExternalEventsFunct inFetchCallback,
NotifyExternalEventsDeliveredFunct inNotifyCallback,
NotifyExternalEventsEvictedFunct inEvictedCallback,
size_t inNumEvents, event_id_t * outLastEventID)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
ExternalEvents ev;
CircularEventBuffer * buf = GetImportanceBuffer(inImportance);
CircularTLVWriter writer;
Platform::CriticalSectionEnter();
WeaveCircularTLVBuffer checkpoint = mEventBuffer->mBuffer;
VerifyOrExit(inFetchCallback != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
VerifyOrExit(inNumEvents > 0, err = WEAVE_ERROR_INVALID_ARGUMENT);
ev.mFirstEventID = buf->VendEventID();
ev.mLastEventID = ev.mFirstEventID;
// need to vend event IDs in a batch.
for (size_t i = 1; i < inNumEvents; i++)
{
ev.mLastEventID = buf->VendEventID();
}
ev.mFetchEventsFunct = inFetchCallback;
ev.mNotifyEventsDeliveredFunct = inNotifyCallback;
ev.mNotifyEventsEvictedFunct = inEvictedCallback;
// We know the size of the event, ensure we have the space for it.
err = EnsureSpace(sizeof(ExternalEvents) + EVENT_CONTAINER_OVERHEAD_TLV_SIZE + IMPORTANCE_TLV_SIZE +
EXTERNAL_EVENT_BYTE_STRING_TLV_SIZE);
SuccessOrExit(err);
checkpoint = mEventBuffer->mBuffer;
writer.Init(&(mEventBuffer->mBuffer));
// can't quite use the BlitEvent method, use the specially created one
err = BlitExternalEvent(writer, inImportance, ev);
mBytesWritten += writer.GetLengthWritten();
exit:
if (err != WEAVE_NO_ERROR)
{
mEventBuffer->mBuffer = checkpoint;
}
else
{
if (outLastEventID != NULL)
{
*outLastEventID = ev.mLastEventID;
}
}
Platform::CriticalSectionExit();
return err;
}
WEAVE_ERROR LoggingManagement::BlitExternalEvent(nl::Weave::TLV::TLVWriter & inWriter, ImportanceType inImportance,
ExternalEvents & inEvents)
{
WEAVE_ERROR err;
TLVType containerType;
err = inWriter.StartContainer(AnonymousTag, kTLVType_Structure, containerType);
SuccessOrExit(err);
// Importance
err = inWriter.Put(ContextTag(kTag_EventImportance), static_cast<uint16_t>(inImportance));
SuccessOrExit(err);
// External event structure, blitted to the buffer as a byte string. Must match the call in
// UnregisterEventCallbackForImportance
err = inWriter.PutBytes(ContextTag(kTag_ExternalEventStructure), static_cast<const uint8_t *>(static_cast<void *>(&inEvents)),
sizeof(ExternalEvents));
SuccessOrExit(err);
err = inWriter.EndContainer(containerType);
SuccessOrExit(err);
err = inWriter.Finalize();
SuccessOrExit(err);
exit:
return err;
}
/**
* @brief
* The public API for unregistering a set of externally stored events.
*
* Unregistering the callback will prevent LoggingManagement from calling
* the callback for a set of events. LoggingManagement will no longer send
* those event IDs to subscribers.
*
* The intent is for one function to serve a set of events at a time. If
* a new set of events need to be registered using the same function, the
* callback should first be unregistered, then registered again. This
* means the original set of events can no longer be fetched.
*
* This function succeeds unconditionally. If the callback was never
* registered or was already unregistered, it's a no-op.
*
* @param[in] inImportance Importance level
* @param[in] inEventID An event ID corresponding to any of the events in the external event block to be unregistered.
*/
void LoggingManagement::UnregisterEventCallbackForImportance(ImportanceType inImportance, event_id_t inEventID)
{
ExternalEvents ev;
WEAVE_ERROR err = WEAVE_NO_ERROR;
TLVReader reader;
WeaveCircularTLVBuffer * readBuffer;
TLVType containerType;
uint8_t * dataPtr;
Platform::CriticalSectionEnter();
err = GetExternalEventsFromEventId(inImportance, inEventID, &ev, reader);
SuccessOrExit(err);
dataPtr = const_cast<uint8_t *>(reader.GetReadPoint());
readBuffer = static_cast<WeaveCircularTLVBuffer *>((void *) reader.GetBufHandle());
// The data pointer is positioned immediately after the element head. The
// element in question, an anonymous structure, has element head of size 1.
// move the pointer back by 1, accounting for the details of the circular
// buffer.
if (readBuffer->GetQueue() != dataPtr)
{
dataPtr -= 1;
}
else
{
dataPtr = readBuffer->GetQueue() + readBuffer->GetQueueSize() - 1;
}
if (ev.IsValid())
{
// Reader is positioned on the external event element.
WeaveCircularTLVBuffer writeBuffer(readBuffer->GetQueue(), readBuffer->GetQueueSize(), dataPtr);
CircularTLVWriter writer;
VerifyOrExit(reader.GetTag() == AnonymousTag, );
VerifyOrExit(reader.GetType() == kTLVType_Structure, );
err = reader.EnterContainer(containerType);
SuccessOrExit(err);
err = reader.Next(kTLVType_UnsignedInteger, ContextTag(kTag_EventImportance));
SuccessOrExit(err);
err = reader.Next(kTLVType_ByteString, ContextTag(kTag_ExternalEventStructure));
SuccessOrExit(err);
// At this point, the reader is positioned correctly, and dataPtr points to the beginning of the string
ev.mFetchEventsFunct = NULL;
ev.mNotifyEventsDeliveredFunct = NULL;
ev.mNotifyEventsEvictedFunct = NULL;
writer.Init(&writeBuffer);
err = BlitExternalEvent(writer, inImportance, ev);
}
exit:
Platform::CriticalSectionExit();
}
#endif // WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
// Internal API used in copying an event out of the event buffers
WEAVE_ERROR LoggingManagement::CopyAndAdjustDeltaTime(const TLVReader & aReader, size_t aDepth, void * aContext)
{
WEAVE_ERROR err;
CopyAndAdjustDeltaTimeContext * ctx = static_cast<CopyAndAdjustDeltaTimeContext *>(aContext);
TLVReader reader(aReader);
if (aReader.GetTag() == nl::Weave::TLV::ContextTag(kTag_EventDeltaSystemTime))
{
if (ctx->mContext->mFirst) // First event gets a timestamp, subsequent ones get a delta T
{
err = ctx->mWriter->Put(nl::Weave::TLV::ContextTag(kTag_EventSystemTimestamp), ctx->mContext->mCurrentTime);
}
else
{
err = ctx->mWriter->CopyElement(reader);
}
}
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
else if (aReader.GetTag() == ContextTag(kTag_EventDeltaUTCTime))
{
if (ctx->mContext->mFirstUtc)
{
err = ctx->mWriter->Put(ContextTag(kTag_EventUTCTimestamp), ctx->mContext->mCurrentUTCTime);
ctx->mContext->mFirstUtc = false;
}
else
{
err = ctx->mWriter->CopyElement(reader);
}
}
#endif // WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
else
{
err = ctx->mWriter->CopyElement(reader);
}
// First event in the sequence gets a eventID neatly packaged
// right after the importance to keep tags ordered
if (aReader.GetTag() == nl::Weave::TLV::ContextTag(kTag_EventImportance))
{
if (ctx->mContext->mFirst)
{
err = ctx->mWriter->Put(nl::Weave::TLV::ContextTag(kTag_EventID), ctx->mContext->mCurrentEventID);
}
}
return err;
}
/**
* @brief
* Log an event via a callback, with options.
*
* The function logs an event represented as an ::EventWriterFunct and
* an app-specific `appData` context. The function writes the event
* metadata and calls the `inEventWriter` with an nl::Weave::TLV::TLVWriter
* reference and `inAppData` context so that the user code can emit
* the event data directly into the event log. This form of event
* logging minimizes memory consumption, as event data is serialized
* directly into the target buffer. The event data MUST contain
* context tags to be interpreted within the schema identified by
* `inProfileID` and `inEventType`. The tag of the first element will be
* ignored; the event logging system will replace it with the
* eventData tag.
*
* The event is logged if the schema importance exceeds the logging
* threshold specified in the LoggingConfiguration. If the event's
* importance does not meet the current threshold, it is dropped and
* the function returns a `0` as the resulting event ID.
*
* This variant of the invocation permits the caller to set any
* combination of `EventOptions`:
* - timestamp, when 0 defaults to the current time at the point of
* the call,
* - "root" section of the event source (event source and trait ID);
* if NULL, it defaults to the current device. the event is marked as
* relating to the device that is making the call,
* - a related event ID for grouping event IDs; when the related event
* ID is 0, the event is marked as not relating to any other events,
* - urgency; by default non-urgent.
*
* @param[in] inSchema Schema defining importance, profile ID, and
* structure type of this event.
*
* @param[in] inEventWriter The callback to invoke to actually
* serialize the event data
*
* @param[in] inAppData Application context for the callback.
*
* @param[in] inOptions The options for the event metadata. May be NULL.
*
* @return event_id_t The event ID if the event was written to the
* log, 0 otherwise.
*/
event_id_t LoggingManagement::LogEvent(const EventSchema & inSchema, EventWriterFunct inEventWriter, void * inAppData,
const EventOptions * inOptions)
{
event_id_t event_id = 0;
Platform::CriticalSectionEnter();
// Make sure we're alive.
VerifyOrExit(mState != kLoggingManagementState_Shutdown, /* no-op */);
event_id = LogEventPrivate(inSchema, inEventWriter, inAppData, inOptions);
exit:
Platform::CriticalSectionExit();
return event_id;
}
// Note: the function below must be called with the critical section
// locked, and only when the logger is not shutting down
inline event_id_t LoggingManagement::LogEventPrivate(const EventSchema & inSchema, EventWriterFunct inEventWriter, void * inAppData,
const EventOptions * inOptions)
{
event_id_t event_id = 0;
CircularTLVWriter writer;
WEAVE_ERROR err = WEAVE_NO_ERROR;
size_t requestSize = WEAVE_CONFIG_EVENT_SIZE_RESERVE;
bool didWriteEvent = false;
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
int32_t ev_opts_deltatime = 0;
#endif // WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
WeaveCircularTLVBuffer checkpoint = mEventBuffer->mBuffer;
EventLoadOutContext ctxt =
EventLoadOutContext(writer, inSchema.mImportance, GetImportanceBuffer(inSchema.mImportance)->mLastEventID, NULL);
EventOptions opts = EventOptions(static_cast<timestamp_t>(System::Timer::GetCurrentEpoch()));
// check whether the entry is to be logged or discarded silently
VerifyOrExit(inSchema.mImportance <= GetCurrentImportance(inSchema.mProfileId), /* no-op */);
// Create all event specific data
// Timestamp; encoded as a delta time
if ((inOptions != NULL) && (inOptions->timestampType == kTimestampType_System))
{
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
ev_opts_deltatime = inOptions->timestamp.systemTimestamp - opts.timestamp.systemTimestamp;
#endif
opts.timestamp.systemTimestamp = inOptions->timestamp.systemTimestamp;
}
if (GetImportanceBuffer(inSchema.mImportance)->mFirstEventTimestamp == 0)
{
GetImportanceBuffer(inSchema.mImportance)->AddEvent(opts.timestamp.systemTimestamp);
}
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
// UTC timestamp; encoded as a delta time
if ((inOptions != NULL) && (inOptions->timestampType == kTimestampType_UTC))
{
opts.timestamp.utcTimestamp = inOptions->timestamp.utcTimestamp;
opts.timestampType = kTimestampType_UTC;
}
else
{
uint64_t utc_tmp;
err = System::Layer::GetClock_RealTimeMS(utc_tmp);
if ((err == WEAVE_NO_ERROR) && (utc_tmp != 0))
{
opts.timestamp.utcTimestamp = static_cast<utc_timestamp_t>(utc_tmp + ev_opts_deltatime);
opts.timestampType = kTimestampType_UTC;
}
}
if ((opts.timestampType == kTimestampType_UTC) && (GetImportanceBuffer(inSchema.mImportance)->mFirstEventUTCTimestamp == 0))
{
GetImportanceBuffer(inSchema.mImportance)->AddEventUTC(opts.timestamp.utcTimestamp);
}
#endif // WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
if (inOptions != NULL)
{
opts.eventSource = inOptions->eventSource;
opts.relatedEventID = inOptions->relatedEventID;
opts.relatedImportance = inOptions->relatedImportance;
}
ctxt.mFirst = false;
ctxt.mCurrentEventID = GetImportanceBuffer(inSchema.mImportance)->mLastEventID;
ctxt.mCurrentTime = GetImportanceBuffer(inSchema.mImportance)->mLastEventTimestamp;
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
ctxt.mCurrentUTCTime = GetImportanceBuffer(inSchema.mImportance)->mLastEventUTCTimestamp;
#endif // WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
// Begin writing
while (!didWriteEvent)
{
// Ensure we have space in the in-memory logging queues
err = EnsureSpace(requestSize);
// If we fail to ensure the initial reserve size, then the
// logging subsystem will never be able to make progress. In
// that case, it is best to assert
if ((requestSize == WEAVE_CONFIG_EVENT_SIZE_RESERVE) && (err != WEAVE_NO_ERROR))
WeaveDie();
SuccessOrExit(err);
// save a checkpoint for the underlying buffer. Note that with
// the current event buffering scheme, only the mEventBuffer will
// be affected by the writes to the `writer` below, and thus
// that's the only thing we need to checkpoint.
checkpoint = mEventBuffer->mBuffer;
// Start the event container (anonymous structure) in the circular buffer
writer.Init(&(mEventBuffer->mBuffer));
err = BlitEvent(&ctxt, inSchema, inEventWriter, inAppData, &opts);
if (err == WEAVE_ERROR_NO_MEMORY)
{
// try again
err = WEAVE_NO_ERROR;
requestSize += WEAVE_CONFIG_EVENT_SIZE_INCREMENT;
mEventBuffer->mBuffer = checkpoint;
continue;
}
didWriteEvent = true;
}
{
// Check the number of bytes written. If the event is loo large
// to be evicted from subsequent buffers, drop it now.
CircularEventBuffer * buffer = mEventBuffer;
do
{
VerifyOrExit((WDM_MAX_NOTIFICATION_SIZE - WDM_NOTIFY_REQUEST_META_INFO_BYTES_MAX) >= writer.GetLengthWritten(),
err = WEAVE_ERROR_WDM_EVENT_TOO_BIG);
VerifyOrExit(buffer->mBuffer.GetQueueSize() >= writer.GetLengthWritten(), err = WEAVE_ERROR_BUFFER_TOO_SMALL);
if (buffer->IsFinalDestinationForImportance(inSchema.mImportance))
break;
else
buffer = buffer->mNext;
} while (true);
}
mBytesWritten += writer.GetLengthWritten();
exit:
if (err != WEAVE_NO_ERROR)
{
mEventBuffer->mBuffer = checkpoint;
WeaveLogError(EventLogging, "Failed to log event for profile id: 0x%x (err: %d)", inSchema.mProfileId, err);
}
else if (inSchema.mImportance <= GetCurrentImportance(inSchema.mProfileId))
{
event_id = GetImportanceBuffer(inSchema.mImportance)->VendEventID();
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
if (opts.timestampType == kTimestampType_UTC)
{
GetImportanceBuffer(inSchema.mImportance)->AddEventUTC(opts.timestamp.utcTimestamp);
#if WEAVE_CONFIG_EVENT_LOGGING_VERBOSE_DEBUG_LOGS
WeaveLogDetail(
EventLogging, "LogEvent event id: %u importance: %u profile id: 0x%x structure id: 0x%x utc timestamp: 0x%" PRIx64,
event_id, inSchema.mImportance, inSchema.mProfileId, inSchema.mStructureType, opts.timestamp.utcTimestamp);
#endif // WEAVE_CONFIG_EVENT_LOGGING_VERBOSE_DEBUG_LOGS
}
else
#endif // WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
{
GetImportanceBuffer(inSchema.mImportance)->AddEvent(opts.timestamp.systemTimestamp);
#if WEAVE_CONFIG_EVENT_LOGGING_VERBOSE_DEBUG_LOGS
WeaveLogDetail(
EventLogging, "LogEvent event id: %u importance: %u profile id: 0x%x structure id: 0x%x sys timestamp: 0x%" PRIx32,
event_id, inSchema.mImportance, inSchema.mProfileId, inSchema.mStructureType, opts.timestamp.systemTimestamp);
#endif // WEAVE_CONFIG_EVENT_LOGGING_VERBOSE_DEBUG_LOGS
}
ScheduleFlushIfNeeded(inOptions == NULL ? false : inOptions->urgent);
}
return event_id;
}
/**
* @brief
* ThrottleLogger elevates the effective logging level to the Production level.
*
*/
void LoggingManagement::ThrottleLogger(void)
{
WeaveLogProgress(EventLogging, "LogThrottle on");
__sync_add_and_fetch(&mThrottled, 1);
}
/**
* @brief
* UnthrottleLogger restores the effective logging level to the configured logging level.
*
*/
void LoggingManagement::UnthrottleLogger(void)
{
uint32_t throttled = __sync_sub_and_fetch(&mThrottled, 1);
if (throttled == 0)
{
WeaveLogProgress(EventLogging, "LogThrottle off");
}
}
// internal API, used to copy events to external buffers
WEAVE_ERROR LoggingManagement::CopyEvent(const TLVReader & aReader, TLVWriter & aWriter, EventLoadOutContext * aContext)
{
TLVReader reader;
TLVType containerType;
CopyAndAdjustDeltaTimeContext context(&aWriter, aContext);
const bool recurse = false;
WEAVE_ERROR err = WEAVE_NO_ERROR;
reader.Init(aReader);
err = reader.EnterContainer(containerType);
SuccessOrExit(err);
err = aWriter.StartContainer(AnonymousTag, kTLVType_Structure, containerType);
SuccessOrExit(err);
err = nl::Weave::TLV::Utilities::Iterate(reader, CopyAndAdjustDeltaTime, &context, recurse);
VerifyOrExit(err == WEAVE_NO_ERROR || err == WEAVE_END_OF_TLV, );
err = aWriter.EndContainer(containerType);
SuccessOrExit(err);
err = aWriter.Finalize();
exit:
return err;
}
#if WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
WEAVE_ERROR LoggingManagement::FindExternalEvents(const TLVReader & aReader, size_t aDepth, void * aContext)
{
WEAVE_ERROR err;
EventLoadOutContext * context = static_cast<EventLoadOutContext *>(aContext);
err = EventIterator(aReader, aDepth, aContext);
if (err == WEAVE_EVENT_ID_FOUND)
{
err = WEAVE_NO_ERROR;
}
if ((err == WEAVE_END_OF_TLV) && context->mExternalEvents->IsValid())
{
err = WEAVE_ERROR_MAX;
}
return err;
}
#endif // WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
/**
* @brief Internal iterator function used to scan and filter though event logs
*
* The function is used to scan through the event log to find events matching the spec in the supplied context.
*/
WEAVE_ERROR LoggingManagement::EventIterator(const TLVReader & aReader, size_t aDepth, void * aContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
const bool recurse = false;
TLVReader innerReader;
TLVType tlvType;
EventEnvelopeContext event;
EventLoadOutContext * loadOutContext = static_cast<EventLoadOutContext *>(aContext);
#if WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
event.mExternalEvents = loadOutContext->mExternalEvents;
if (event.mExternalEvents != NULL)
{
event.mExternalEvents->Invalidate();
}
#endif // WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
innerReader.Init(aReader);
err = innerReader.EnterContainer(tlvType);
SuccessOrExit(err);
err = nl::Weave::TLV::Utilities::Iterate(innerReader, FetchEventParameters, &event, recurse);
VerifyOrExit(event.mNumFieldsToRead == 0, err = WEAVE_NO_ERROR);
err = WEAVE_NO_ERROR;
if (event.mImportance == loadOutContext->mImportance)
{
#if WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
if ((event.mExternalEvents != NULL) && (event.mExternalEvents->IsValid()))
{
// external event structure for the thing we want to read
// out. if there is a chance this is to be written out by
// the app, kick it up to FetchEventsSince, otherwise skip
// over the block of external events.
// if we're in the process of writing, kick it up to FetchEventsSince
VerifyOrExit(loadOutContext->mCurrentEventID < loadOutContext->mStartingEventID, err = WEAVE_END_OF_TLV);
// if the external events are of interest, kick it up to the caller
VerifyOrExit(event.mExternalEvents->mLastEventID < loadOutContext->mStartingEventID, err = WEAVE_END_OF_TLV);
// otherwise, skip over the block of external events
loadOutContext->mCurrentEventID = event.mExternalEvents->mLastEventID + 1;
}
else
#endif // WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
{
loadOutContext->mCurrentTime += event.mDeltaTime;
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
loadOutContext->mCurrentUTCTime += event.mDeltaUtc;
#endif // WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
VerifyOrExit(loadOutContext->mCurrentEventID < loadOutContext->mStartingEventID, err = WEAVE_EVENT_ID_FOUND);
loadOutContext->mCurrentEventID++;
}
}
exit:
return err;
}
/**
* @brief
* Internal API used to implement #FetchEventsSince
*
* Iterator function to used to copy an event from the log into a
* TLVWriter. The included aContext contains the context of the copy
* operation, including the TLVWriter that will hold the copy of an
* event. If event cannot be written as a whole, the TLVWriter will
* be rolled back to event boundary.
*
* @retval #WEAVE_END_OF_TLV Function reached the end of the event
* @retval #WEAVE_ERROR_NO_MEMORY Function could not write a portion of
* the event to the TLVWriter.
* @retval #WEAVE_ERROR_BUFFER_TOO_SMALL Function could not write a
* portion of the event to the TLVWriter.
*/
WEAVE_ERROR LoggingManagement::CopyEventsSince(const TLVReader & aReader, size_t aDepth, void * aContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
nl::Weave::TLV::TLVWriter checkpoint;
EventLoadOutContext * loadOutContext = static_cast<EventLoadOutContext *>(aContext);
err = EventIterator(aReader, aDepth, aContext);
if (err == WEAVE_EVENT_ID_FOUND)
{
// checkpoint the writer
checkpoint = loadOutContext->mWriter;
err = CopyEvent(aReader, loadOutContext->mWriter, loadOutContext);
// WEAVE_NO_ERROR and WEAVE_END_OF_TLV signify a
// successful copy. In all other cases, roll back the
// writer state back to the checkpoint, i.e., the state
// before we began the copy operation.
VerifyOrExit((err == WEAVE_NO_ERROR) || (err == WEAVE_END_OF_TLV), loadOutContext->mWriter = checkpoint);
loadOutContext->mCurrentTime = 0;
loadOutContext->mFirst = false;
loadOutContext->mCurrentEventID++;
}
exit:
return err;
}
/**
* @brief
* A function to retrieve events of specified importance since a specified event ID.
*
* Given a nl::Weave::TLV::TLVWriter, an importance type, and an event ID, the
* function will fetch events of specified importance since the
* specified event. The function will continue fetching events until
* it runs out of space in the nl::Weave::TLV::TLVWriter or in the log. The function
* will terminate the event writing on event boundary.
*
* @param[in] ioWriter The writer to use for event storage
*
* @param[in] inImportance The importance of events to be fetched
*
* @param[inout] ioEventID On input, the ID of the event immediately
* prior to the one we're fetching. On
* completion, the ID of the last event
* fetched.
*
* @retval #WEAVE_END_OF_TLV The function has reached the end of the
* available log entries at the specified
* importance level
*
* @retval #WEAVE_ERROR_NO_MEMORY The function ran out of space in the
* ioWriter, more events in the log are
* available.
*
* @retval #WEAVE_ERROR_BUFFER_TOO_SMALL The function ran out of space in the
* ioWriter, more events in the log are
* available.
*
*/
WEAVE_ERROR LoggingManagement::FetchEventsSince(TLVWriter & ioWriter, ImportanceType inImportance, event_id_t & ioEventID)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
const bool recurse = false;
TLVReader reader;
#if WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
ExternalEvents ev;
EventLoadOutContext aContext(ioWriter, inImportance, ioEventID, &ev);
#else
EventLoadOutContext aContext(ioWriter, inImportance, ioEventID, NULL);
#endif // WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
CircularEventBuffer * buf = mEventBuffer;
Platform::CriticalSectionEnter();
while (!buf->IsFinalDestinationForImportance(inImportance))
{
buf = buf->mNext;
}
aContext.mCurrentTime = buf->mFirstEventTimestamp;
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
aContext.mCurrentUTCTime = buf->mFirstEventUTCTimestamp;
#endif // WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
aContext.mCurrentEventID = buf->mFirstEventID;
err = GetEventReader(reader, inImportance);
SuccessOrExit(err);
err = nl::Weave::TLV::Utilities::Iterate(reader, CopyEventsSince, &aContext, recurse);
#if WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
if ((err == WEAVE_END_OF_TLV) && (ev.IsValid()))
{
if (ev.mFetchEventsFunct != NULL)
{
err = ev.mFetchEventsFunct(&aContext);
}
else
{
aContext.mCurrentEventID = ev.mLastEventID + 1;
err = WEAVE_END_OF_TLV;
}
}
#endif // WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
exit:
ioEventID = aContext.mCurrentEventID;
Platform::CriticalSectionExit();
return err;
}
/**
* @brief
* A helper method useful for examining the in-memory log buffers
*
* @param[inout] ioReader A reference to the reader that will be
* initialized with the backing storage from
* the event log
*
* @param[in] inImportance The starting importance for the reader.
* Note that in this case the starting
* importance is somewhat counter intuitive:
* more important events share the buffers
* with less important events, in addition to
* their dedicated buffers. As a result, the
* reader will traverse the least data when
* the Debug importance is passed in.
*
* @return #WEAVE_NO_ERROR Unconditionally.
*/
WEAVE_ERROR LoggingManagement::GetEventReader(TLVReader & ioReader, ImportanceType inImportance)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
CircularEventBuffer * buffer;
CircularEventReader reader;
for (buffer = mEventBuffer; buffer != NULL && !buffer->IsFinalDestinationForImportance(inImportance); buffer = buffer->mNext)
;
VerifyOrExit(buffer != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
reader.Init(buffer);
ioReader.Init(reader);
exit:
return err;
}
// internal API
WEAVE_ERROR LoggingManagement::FetchEventParameters(const TLVReader & aReader, size_t aDepth, void * aContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
EventEnvelopeContext * envelope = static_cast<EventEnvelopeContext *>(aContext);
TLVReader reader;
uint16_t extImportance; // Note: the type here matches the type case in LoggingManagement::LogEvent, importance section
reader.Init(aReader);
VerifyOrExit(envelope->mNumFieldsToRead > 0, err = WEAVE_END_OF_TLV);
if ((reader.GetTag() == nl::Weave::TLV::ContextTag(kTag_ExternalEventStructure)) && (envelope->mExternalEvents != NULL))
{
err = reader.GetBytes(static_cast<uint8_t *>(static_cast<void *>(envelope->mExternalEvents)), sizeof(ExternalEvents));
VerifyOrExit(err == WEAVE_NO_ERROR, *(envelope->mExternalEvents) = ExternalEvents());
envelope->mNumFieldsToRead--;
}
if (reader.GetTag() == nl::Weave::TLV::ContextTag(kTag_EventImportance))
{
err = reader.Get(extImportance);
SuccessOrExit(err);
envelope->mImportance = static_cast<ImportanceType>(extImportance);
envelope->mNumFieldsToRead--;
}
if (reader.GetTag() == nl::Weave::TLV::ContextTag(kTag_EventDeltaSystemTime))
{
err = reader.Get(envelope->mDeltaTime);
SuccessOrExit(err);
envelope->mNumFieldsToRead--;
}
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
if (reader.GetTag() == nl::Weave::TLV::ContextTag(kTag_EventDeltaUTCTime))
{
err = reader.Get(envelope->mDeltaUtc);
SuccessOrExit(err);
envelope->mNumFieldsToRead--;
}
#endif // WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
exit:
return err;
}
// internal API: determine importance of an event, and the space the event requires
WEAVE_ERROR LoggingManagement::EvictEvent(WeaveCircularTLVBuffer & inBuffer, void * inAppData, TLVReader & inReader)
{
ReclaimEventCtx * ctx = static_cast<ReclaimEventCtx *>(inAppData);
CircularEventBuffer * eventBuffer = ctx->mEventBuffer;
TLVType containerType;
EventEnvelopeContext context;
const bool recurse = false;
WEAVE_ERROR err;
ImportanceType imp = kImportanceType_Invalid;
#if WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
ExternalEvents ev;
ev.Invalidate();
context.mExternalEvents = &ev;
#else
context.mExternalEvents = NULL;
#endif // WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
// pull out the delta time, pull out the importance
err = inReader.Next();
SuccessOrExit(err);
err = inReader.EnterContainer(containerType);
SuccessOrExit(err);
nl::Weave::TLV::Utilities::Iterate(inReader, FetchEventParameters, &context, recurse);
err = inReader.ExitContainer(containerType);
SuccessOrExit(err);
imp = static_cast<ImportanceType>(context.mImportance);
if (eventBuffer->IsFinalDestinationForImportance(imp))
{
// event is getting dropped. Increase the eventid and first timestamp.
size_t numEventsToDrop = 1;
#if WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
if (ev.IsValid())
{
numEventsToDrop = ev.mLastEventID - ev.mFirstEventID + 1;
if (ev.mNotifyEventsEvictedFunct != NULL)
{
ev.mNotifyEventsEvictedFunct(&ev);
}
}
#endif // WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
eventBuffer->RemoveEvent(numEventsToDrop);
eventBuffer->mFirstEventTimestamp += context.mDeltaTime;
WeaveLogProgress(EventLogging, "Dropped events do to overflow: { importance_level: %d, count: %d };", imp, numEventsToDrop);
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
eventBuffer->mFirstEventUTCTimestamp += context.mDeltaUtc;
#endif // WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
ctx->mSpaceNeededForEvent = 0;
}
else
{
// event is not getting dropped. Note how much space it requires, and return.
ctx->mSpaceNeededForEvent = inReader.GetLengthRead();
err = WEAVE_END_OF_TLV;
}
exit:
return err;
}
// Notes: called as a result of the timer expiration. Main job:
// figure out whether trigger still applies, if it does, then kick off
// the upload. If it does not, perform the appropriate backoff.
void LoggingManagement::LoggingFlushHandler(System::Layer * systemLayer, void * appState, INET_ERROR err)
{
LoggingManagement * logger = static_cast<LoggingManagement *>(appState);
logger->FlushHandler(systemLayer, err);
}
// FlushHandler is only called by the Weave thread. As such, guard variables
// do not need to be atomically set or checked.
void LoggingManagement::FlushHandler(System::Layer * inSystemLayer, INET_ERROR inErr)
{
#if WEAVE_CONFIG_EVENT_LOGGING_BDX_OFFLOAD
const LoggingConfiguration & config = LoggingConfiguration::GetInstance();
#endif // WEAVE_CONFIG_EVENT_LOGGING_BDX_OFFLOAD
switch (mState)
{
case kLoggingManagementState_Idle: {
#if WEAVE_CONFIG_EVENT_LOGGING_BDX_OFFLOAD
// Nothing prevents a flush. If the configuration supports
// it, transition into "in progress" state, and kick off the
// offload process. If no valid upload location exists,
// schedule an upload at the maximum upload interval
if ((mBDXUploader != NULL) && (config.GetDestNodeId() != kAnyNodeId))
{
WEAVE_ERROR err;
mState = kLoggingManagementState_InProgress;
err = mBDXUploader->StartUpload(config.GetDestNodeId(), config.GetDestNodeIPAddress());
if (err != WEAVE_NO_ERROR)
WeaveLogError(EventLogging, "Failed to start BDX (err: %d)", err);
}
else
{
if (mExchangeMgr != NULL)
{
mExchangeMgr->MessageLayer->SystemLayer->StartTimer(config.mMaximumLogUploadInterval, LoggingFlushHandler, this);
}
}
#endif // WEAVE_CONFIG_EVENT_LOGGING_BDX_OFFLOAD
#if WEAVE_CONFIG_EVENT_LOGGING_WDM_OFFLOAD
if (mExchangeMgr != NULL)
{
nl::Weave::Profiles::DataManagement::SubscriptionEngine::GetInstance()->GetNotificationEngine()->Run();
mUploadRequested = false;
}
#endif // WEAVE_CONFIG_EVENT_LOGGING_WDM_OFFLOAD
break;
}
case kLoggingManagementState_Holdoff: {
#if WEAVE_CONFIG_EVENT_LOGGING_BDX_OFFLOAD
mState = kLoggingManagementState_Idle;
mUploadRequested = false;
ScheduleFlushIfNeeded(false);
if (mUploadRequested == false)
{
if (mExchangeMgr != NULL)
{
mExchangeMgr->MessageLayer->SystemLayer->StartTimer(config.mMaximumLogUploadInterval, LoggingFlushHandler, this);
}
}
#endif // WEAVE_CONFIG_EVENT_LOGGING_BDX_OFFLOAD
break;
}
case kLoggingManagementState_InProgress:
case kLoggingManagementState_Shutdown: {
// should never end in these states in this function
break;
}
}
}
void LoggingManagement::SignalUploadDone(void)
{
#if WEAVE_CONFIG_EVENT_LOGGING_BDX_OFFLOAD
const LoggingConfiguration & config = LoggingConfiguration::GetInstance();
if (mState == kLoggingManagementState_InProgress)
{
mState = kLoggingManagementState_Holdoff;
if (mExchangeMgr != NULL)
{
mExchangeMgr->MessageLayer->SystemLayer->StartTimer(config.mMinimumLogUploadInterval, LoggingFlushHandler, this);
}
}
#endif // WEAVE_CONFIG_EVENT_LOGGING_BDX_OFFLOAD
}
/**
* @brief
* Schedule a log offload task.
*
* The function decides whether to schedule a task offload process,
* and if so, it schedules the `LoggingFlushHandler` to be run
* asynchronously on the Weave thread.
*
* The decision to schedule a flush is dependent on three factors:
*
* -- an explicit request to flush the buffer
*
* -- the state of the event buffer and the amount of data not yet
* synchronized with the event consumers
*
* -- whether there is an already pending request flush request event.
*
* The explicit request to schedule a flush is passed via an input
* parameter.
*
* The automatic flush is typically scheduled when the event buffers
* contain enough data to merit starting a new offload. Additional
* triggers -- such as minimum and maximum time between offloads --
* may also be taken into account depending on the offload strategy.
*
* The pending state of the event log is indicated by
* `mUploadRequested` variable. Since this function can be called by
* multiple threads, `mUploadRequested` must be atomically read and
* set, to avoid scheduling a redundant `LoggingFlushHandler` before
* the notification has been sent.
*
* @param inRequestFlush A boolean value indicating whether the flush
* should be scheduled regardless of internal
* buffer management policy.
*
* @retval #WEAVE_ERROR_INCORRECT_STATE LoggingManagement module was not initialized fully.
* @retval #WEAVE_NO_ERROR On success.
*/
WEAVE_ERROR LoggingManagement::ScheduleFlushIfNeeded(bool inRequestFlush)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
#if WEAVE_CONFIG_EVENT_LOGGING_BDX_OFFLOAD
inRequestFlush |= CheckShouldRunBDX();
#endif // WEAVE_CONFIG_EVENT_LOGGING_BDX_OFFLOAD
#if WEAVE_CONFIG_EVENT_LOGGING_WDM_OFFLOAD
inRequestFlush |= CheckShouldRunWDM();
#endif // WEAVE_CONFIG_EVENT_LOGGING_WDM_OFFLOAD
if (inRequestFlush && __sync_bool_compare_and_swap(&mUploadRequested, false, true))
{
if ((mExchangeMgr != NULL) && (mExchangeMgr->MessageLayer != NULL) && (mExchangeMgr->MessageLayer->SystemLayer != NULL))
{
mExchangeMgr->MessageLayer->SystemLayer->ScheduleWork(LoggingFlushHandler, this);
}
else
{
err = WEAVE_ERROR_INCORRECT_STATE;
mUploadRequested = false;
}
}
return err;
}
#if WEAVE_CONFIG_EVENT_LOGGING_BDX_OFFLOAD
bool LoggingManagement::CheckShouldRunBDX(void)
{
const LoggingConfiguration & config = LoggingConfiguration::GetInstance();
return ((mBDXUploader != NULL) && ((mBytesWritten - mBDXUploader->GetUploadPosition()) > config.mUploadThreshold));
}
#endif // WEAVE_CONFIG_EVENT_LOGGING_BDX_OFFLOAD
#if WEAVE_CONFIG_EVENT_LOGGING_WDM_OFFLOAD
/**
* @brief
* Decide whether to offload events based on the number of bytes in event buffers unscheduled for upload.
*
* The behavior of the function is controlled via
* #WEAVE_CONFIG_EVENT_LOGGING_BYTE_THRESHOLD constant. If the system
* wrote more than that number of bytes since the last time a WDM
* Notification was sent, the function will indicate it is time to
* trigger the NotificationEngine.
*
* @retval true Events should be offloaded
* @retval false Otherwise
*/
bool LoggingManagement::CheckShouldRunWDM(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
size_t minimalBytesOffloaded = mBytesWritten;
bool ret = false;
// Get the minimal log position (in bytes) across all subscribers
err = nl::Weave::Profiles::DataManagement::SubscriptionEngine::GetInstance()->GetMinEventLogPosition(minimalBytesOffloaded);
SuccessOrExit(err);
// return true if we can offload more than the threshold bytes to a subscription
ret = ((minimalBytesOffloaded + WEAVE_CONFIG_EVENT_LOGGING_BYTE_THRESHOLD) < mBytesWritten);
exit:
return ret;
}
#endif // WEAVE_CONFIG_EVENT_LOGGING_WDM_OFFLOAD
WEAVE_ERROR LoggingManagement::SetLoggingEndpoint(event_id_t * inEventEndpoints, size_t inNumImportanceLevels,
size_t & outBytesOffloaded)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
CircularEventBuffer * eventBuffer = mEventBuffer;
Platform::CriticalSectionEnter();
outBytesOffloaded = mBytesWritten;
while (eventBuffer != NULL && inNumImportanceLevels > 0)
{
if ((eventBuffer->mImportance >= kImportanceType_First) &&
((static_cast<size_t>(eventBuffer->mImportance - kImportanceType_First)) < inNumImportanceLevels))
{
inEventEndpoints[eventBuffer->mImportance - kImportanceType_First] = eventBuffer->mLastEventID;
}
eventBuffer = eventBuffer->mNext;
}
Platform::CriticalSectionExit();
return err;
}
/**
* @brief
* Get the total number of bytes written (across all event
* importances) to this log since its instantiation
*
* @returns The number of bytes written to the log.
*/
uint32_t LoggingManagement::GetBytesWritten(void) const
{
return mBytesWritten;
}
void LoggingManagement::NotifyEventsDelivered(ImportanceType inImportance, event_id_t inLastDeliveredEventID,
uint64_t inRecipientNodeID)
{
#if WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
ExternalEvents ev;
WEAVE_ERROR err = WEAVE_NO_ERROR;
TLVReader reader;
event_id_t currentId;
Platform::CriticalSectionEnter();
currentId = GetFirstEventID(inImportance);
while (currentId <= inLastDeliveredEventID)
{
err = GetExternalEventsFromEventId(inImportance, currentId, &ev, reader);
SuccessOrExit(err);
VerifyOrExit(ev.IsValid(), );
VerifyOrExit(ev.mFirstEventID <= inLastDeliveredEventID, );
if (ev.mNotifyEventsDeliveredFunct != NULL)
{
ev.mNotifyEventsDeliveredFunct(&ev, inLastDeliveredEventID, inRecipientNodeID);
}
currentId = ev.mLastEventID + 1;
}
exit:
Platform::CriticalSectionExit();
#endif // WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
}
#if WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
/**
* @brief
* Retrieve ExternalEvent descriptor based on the importance and event ID of the external event.
*
* @param[in] inImportance The importance of the event
* @param[in] inEventID The ID of the event
*
* @param[out] outExternalEvents A pointer to the ExternalEvent structure. If the external event specified via inImportance /
* inEventID tuple corresponds to a valid external ID, the structure will be populated with the
* descriptor holding all relevant information about that particular block of external events.
*
* @param[out] outReader A reference to a TLVReader. On successful retrieval of ExternalEvent structure, the reader will be
* positioned to the beginning of the TLV struct containing the external events.
*
* @retval WEAVE_NO_ERROR On successful retrieval of the ExternalEvents
*
* @retval WEAVE_ERROR_INVALID_ARGUMENT When the arguments passed in do not correspond to an external event, or if the external
* event was already either dropped or unregistered.
*/
WEAVE_ERROR LoggingManagement::GetExternalEventsFromEventId(ImportanceType inImportance, event_id_t inEventID,
ExternalEvents * outExternalEvents, TLVReader & outReader)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint32_t dummyBuf;
const bool recurse = false;
TLVWriter writer;
EventLoadOutContext aContext(writer, inImportance, inEventID, outExternalEvents);
CircularEventBuffer * buf = mEventBuffer;
TLVReader resultReader;
writer.Init(static_cast<uint8_t *>(static_cast<void *>(&dummyBuf)), sizeof(uint32_t));
while (!buf->IsFinalDestinationForImportance(inImportance))
{
buf = buf->mNext;
}
aContext.mCurrentTime = buf->mFirstEventTimestamp;
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
aContext.mCurrentUTCTime = buf->mFirstEventUTCTimestamp;
#endif // WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
aContext.mCurrentEventID = buf->mFirstEventID;
err = GetEventReader(outReader, inImportance);
SuccessOrExit(err);
err = nl::Weave::TLV::Utilities::Find(outReader, FindExternalEvents, &aContext, resultReader, recurse);
if (err == WEAVE_NO_ERROR)
outReader.Init(resultReader);
exit:
return err;
}
#endif // WEAVE_CONFIG_EVENT_LOGGING_EXTERNAL_EVENT_SUPPORT
void LoggingManagement::SetBDXUploader(LogBDXUpload * inUploader)
{
if (mBDXUploader == NULL)
{
mBDXUploader = inUploader;
}
else
{
WeaveLogError(EventLogging, "mBDXUploader already set");
}
}
/**
* @brief
* A constructor for the CircularEventBuffer (internal API).
*
* @param[in] inBuffer The actual storage to use for event storage.
*
* @param[in] inBufferLength The length of the \c inBuffer in bytes.
*
* @param[in] inPrev The pointer to CircularEventBuffer storing
* events of lesser priority.
*
* @param[in] inNext The pointer to CircularEventBuffer storing
* events of greater priority.
*
* @return CircularEventBuffer
*/
CircularEventBuffer::CircularEventBuffer(uint8_t * inBuffer, size_t inBufferLength, CircularEventBuffer * inPrev,
CircularEventBuffer * inNext) :
mBuffer(inBuffer, inBufferLength),
mPrev(inPrev), mNext(inNext), mImportance(kImportanceType_First), mFirstEventID(1), mLastEventID(0), mFirstEventTimestamp(0),
mLastEventTimestamp(0),
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
mFirstEventUTCTimestamp(0), mLastEventUTCTimestamp(0), mUTCInitialized(false),
#endif // WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
mEventIdCounter(NULL)
{
// TODO: hook up the platform-specific persistent event ID.
}
/**
* @brief
* A helper function that determines whether the event of
* specified importance is dropped from this buffer.
*
* @param[in] inImportance Importance of the event.
*
* @retval true The event will be dropped from this buffer as
* a result of queue overflow.
* @retval false The event will be bumped to the next queue.
*/
bool CircularEventBuffer::IsFinalDestinationForImportance(ImportanceType inImportance) const
{
return !((mNext != NULL) && (mNext->mImportance >= inImportance));
}
/**
* @brief
* Given a timestamp of an event, compute the delta time to store in the log
*
* @param inEventTimestamp The event timestamp.
*
* @return int32_t Time delta to encode for the event.
*/
void CircularEventBuffer::AddEvent(timestamp_t inEventTimestamp)
{
if (mFirstEventTimestamp == 0)
{
mFirstEventTimestamp = inEventTimestamp;
mLastEventTimestamp = inEventTimestamp;
}
mLastEventTimestamp = inEventTimestamp;
}
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
/**
* @brief
* Given a timestamp of an event, compute the delta utc time to store in the log
*
* @param inEventTimestamp The event's utc timestamp
*
* @return int64_t Time delta to encode for the event.
*/
void CircularEventBuffer::AddEventUTC(utc_timestamp_t inEventTimestamp)
{
if (mUTCInitialized == false)
{
mFirstEventUTCTimestamp = inEventTimestamp;
mUTCInitialized = true;
}
mLastEventUTCTimestamp = inEventTimestamp;
}
#endif // WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
void CircularEventBuffer::RemoveEvent(size_t aNumEvents)
{
mFirstEventID += aNumEvents;
}
/**
* @brief
* Initializes a TLVReader object backed by CircularEventBuffer
*
* Reading begins in the CircularTLVBuffer belonging to this
* CircularEventBuffer. When the reader runs out of data, it begins
* to read from the previous CircularEventBuffer.
*
* @param[in] inBuf A pointer to a fully initialized CircularEventBuffer
*
*/
void CircularEventReader::Init(CircularEventBuffer * inBuf)
{
CircularTLVReader reader;
CircularEventBuffer * prev;
reader.Init(&inBuf->mBuffer);
TLVReader::Init(reader);
mBufHandle = (uintptr_t) inBuf;
GetNextBuffer = CircularEventBuffer::GetNextBufferFunct;
for (prev = inBuf->mPrev; prev != NULL; prev = prev->mPrev)
{
reader.Init(&prev->mBuffer);
mMaxLen += reader.GetRemainingLength();
}
}
WEAVE_ERROR CircularEventBuffer::SerializeEvents(TLVWriter & writer)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
TLVType container;
uint8_t *inBufStart = mBuffer.QueueHead();
size_t inBufLen = mBuffer.DataLength();
uint8_t *bufStart = mBuffer.GetQueue();
size_t bufLen = mBuffer.GetQueueSize();
size_t initLen = inBufLen;
if (initLen > bufStart + bufLen - inBufStart)
{
initLen = bufStart + bufLen - inBufStart;
}
err = writer.StartContainer(AnonymousTag, kTLVType_Structure, container);
SuccessOrExit(err);
err = writer.Put(ContextTag(kTag_PersistEvent_ImportanceLevel), (uint8_t)mImportance);
SuccessOrExit(err);
err = writer.StartPutBytes(ContextTag(kTag_PersistEvent_EventData), inBufLen);
SuccessOrExit(err);
err = writer.ContinuePutBytes(inBufStart, initLen);
SuccessOrExit(err);
if (inBufLen > initLen)
{
err = writer.ContinuePutBytes(bufStart, inBufLen - initLen);
SuccessOrExit(err);
}
err = writer.Put(ContextTag(kTag_PersistEvent_FirstEventId), mFirstEventID);
SuccessOrExit(err);
err = writer.Put(ContextTag(kTag_PersistEvent_LastEventId), mLastEventID);
SuccessOrExit(err);
err = writer.Put(ContextTag(kTag_PersistEvent_FirstEventTimestamp), mFirstEventTimestamp);
SuccessOrExit(err);
err = writer.Put(ContextTag(kTag_PersistEvent_LastEventTimestamp), mLastEventTimestamp);
SuccessOrExit(err);
err = writer.Put(ContextTag(kTag_PersistEvent_EventIdCounter), mEventIdCounter->GetValue());
SuccessOrExit(err);
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
err = writer.Put(ContextTag(kTag_PersistEvent_FirstEventUTCTimestamp), mFirstEventUTCTimestamp);
SuccessOrExit(err);
err = writer.Put(ContextTag(kTag_PersistEvent_LastEventUTCTimestamp), mLastEventUTCTimestamp);
SuccessOrExit(err);
err = writer.PutBoolean(ContextTag(kTag_PersistEvent_UTCInitialized), mUTCInitialized);
SuccessOrExit(err);
#endif
err = writer.EndContainer(container);
SuccessOrExit(err);
exit:
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(EventLogging, "Serialize event error: %d", err);
}
return err;
}
WEAVE_ERROR CircularEventBuffer::LoadEvents(TLVReader & reader)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
TLVType container;
uint32_t counterValue;
uint8_t importance;
err = reader.Next(kTLVType_Structure, AnonymousTag);
SuccessOrExit(err);
err = reader.EnterContainer(container);
SuccessOrExit(err);
err = reader.Next(kTLVType_UnsignedInteger, ContextTag(kTag_PersistEvent_ImportanceLevel));
SuccessOrExit(err);
err = reader.Get(importance);
SuccessOrExit(err);
mImportance = (ImportanceType)importance;
err = reader.Next(kTLVType_ByteString, ContextTag(kTag_PersistEvent_EventData));
SuccessOrExit(err);
VerifyOrExit(reader.GetLength() <= mBuffer.GetQueueSize(), err = WEAVE_ERROR_BUFFER_TOO_SMALL);
mBuffer.SetQueueLength(reader.GetLength());
mBuffer.SetQueueHead(mBuffer.GetQueue());
err = reader.GetBytes(mBuffer.GetQueue(), mBuffer.DataLength());
SuccessOrExit(err);
err = reader.Next(kTLVType_UnsignedInteger, ContextTag(kTag_PersistEvent_FirstEventId));
SuccessOrExit(err);
err = reader.Get(mFirstEventID);
SuccessOrExit(err);
err = reader.Next(kTLVType_UnsignedInteger, ContextTag(kTag_PersistEvent_LastEventId));
SuccessOrExit(err);
err = reader.Get(mLastEventID);
SuccessOrExit(err);
err = reader.Next(kTLVType_UnsignedInteger, ContextTag(kTag_PersistEvent_FirstEventTimestamp));
SuccessOrExit(err);
err = reader.Get(mFirstEventTimestamp);
SuccessOrExit(err);
err = reader.Next(kTLVType_UnsignedInteger, ContextTag(kTag_PersistEvent_LastEventTimestamp));
SuccessOrExit(err);
err = reader.Get(mLastEventTimestamp);
SuccessOrExit(err);
err = reader.Next(kTLVType_UnsignedInteger, ContextTag(kTag_PersistEvent_EventIdCounter));
SuccessOrExit(err);
err = reader.Get(counterValue);
SuccessOrExit(err);
static_cast<PersistedCounter *>(mEventIdCounter)->SetValue(counterValue);
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
err = reader.Next(kTLVType_UnsignedInteger, ContextTag(kTag_PersistEvent_FirstEventUTCTimestamp));
SuccessOrExit(err);
err = reader.Get(mFirstEventUTCTimestamp);
SuccessOrExit(err);
err = reader.Next(kTLVType_UnsignedInteger, ContextTag(kTag_PersistEvent_LastEventUTCTimestamp));
SuccessOrExit(err);
err = reader.Get(mLastEventUTCTimestamp);
SuccessOrExit(err);
err = reader.Next(kTLVType_Boolean, ContextTag(kTag_PersistEvent_UTCInitialized));
SuccessOrExit(err);
err = reader.Get(mUTCInitialized);
SuccessOrExit(err);
#endif
err = reader.ExitContainer(container);
SuccessOrExit(err);
exit:
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(EventLogging, "Load event error: %d", err);
}
return err;
}
WEAVE_ERROR CircularEventBuffer::GetNextBufferFunct(TLVReader & ioReader, uintptr_t & inBufHandle, const uint8_t *& outBufStart,
uint32_t & outBufLen)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
CircularEventBuffer * buf;
VerifyOrExit(inBufHandle != 0, err = WEAVE_ERROR_INVALID_ARGUMENT);
buf = static_cast<CircularEventBuffer *>((void *) inBufHandle);
err = buf->mBuffer.GetNextBuffer(ioReader, outBufStart, outBufLen);
SuccessOrExit(err);
if ((outBufLen == 0) && (buf->mPrev != NULL))
{
inBufHandle = (uintptr_t) buf->mPrev;
outBufStart = NULL;
err = GetNextBufferFunct(ioReader, inBufHandle, outBufStart, outBufLen);
}
exit:
return err;
}
CopyAndAdjustDeltaTimeContext::CopyAndAdjustDeltaTimeContext(TLVWriter * inWriter, EventLoadOutContext * inContext) :
mWriter(inWriter), mContext(inContext)
{ }
EventEnvelopeContext::EventEnvelopeContext(void) :
mNumFieldsToRead(2), // read out importance and either system or utc delta time. events do not store both deltas.
mDeltaTime(0),
#if WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
mDeltaUtc(0),
#endif // WEAVE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS
mImportance(kImportanceType_First), mExternalEvents(NULL)
{ }
} // namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current)
} // namespace Profiles
} // namespace Weave
} // namespace nl