Merge pull request #628 from aaronokano/cleanup/aaronokano/logging-management-log-typo-fix
LoggingManagement: Fix typo in log message
diff --git a/src/device-manager/cocoa/NLLogging.h b/src/device-manager/cocoa/NLLogging.h
index e0c1f94..baae2ff 100644
--- a/src/device-manager/cocoa/NLLogging.h
+++ b/src/device-manager/cocoa/NLLogging.h
@@ -22,15 +22,53 @@
*
*/
-#if DEBUG
-#define WDM_LOG_DEBUG NSLog
-#define WDM_LOG_ERROR NSLog
+#import <Foundation/Foundation.h>
+#import "NLWeaveLogging.h"
-#define WDM_LOG_METHOD_SIG() ({ NSLog(@"[<%@: %p> %@]", NSStringFromClass([self class]), self, NSStringFromSelector(_cmd)); })
-#else
-// do nothing
+NS_ASSUME_NONNULL_BEGIN
+
+#if DEBUG
+
+/** Name of the logging module for the Objective-C platform-specific component. */
+static NSString * const kNLWeaveDeviceManagerCocoaModuleName = @"DM-Cocoa";
+
+/** Macros for logging Weave messages of various log levels from platform-specific code. */
+#define WDM_LOG_DEBUG(fmt, ...) _WDM_LOG(NLLogLevelDetail, fmt, ##__VA_ARGS__)
+#define WDM_LOG_INFO(fmt, ...) _WDM_LOG(NLLogLevelProgress, fmt, ##__VA_ARGS__)
+#define WDM_LOG_ERROR(fmt, ...) _WDM_LOG(NLLogLevelError, fmt, ##__VA_ARGS__)
+
+/** Macro for logging a method signature (must be called from an Objective-C method). */
+#define WDM_LOG_METHOD_SIG() WDM_LOG_INFO(@"<%@: %p>", NSStringFromClass([self class]), self)
+
+/**
+ * @def _WDM_LOG(logLevel, fmt, ...)
+ *
+ * @brief
+ * Helper macro for formatting Weave logs and delegating log handling to @c NLWeaveLogging.
+ *
+ * @param logLevel An @c NLLogLevel specifying the level of the log message.
+ * @param fmt The log message format.
+ * @param ... A variable number of arguments to be added to the log message format.
+ */
+#define _WDM_LOG(logLevel, fmt, ...) \
+ do { \
+ NSString * formattedMessage = ([NSString \
+ stringWithFormat:@"%s:%d %@", __PRETTY_FUNCTION__, __LINE__, [NSString stringWithFormat:fmt, ##__VA_ARGS__]]); \
+ \
+ [NLWeaveLogging handleWeaveLogFromModule:NLLogModuleCocoa \
+ moduleName:kNLWeaveDeviceManagerCocoaModuleName \
+ level:logLevel \
+ formattedMessage:formattedMessage]; \
+ } while (0);
+
+#else // DEBUG
+
+// Do nothing – strip logs from release builds.
#define WDM_LOG_DEBUG(...)
+#define WDM_LOG_INFO(...)
#define WDM_LOG_ERROR(...)
#define WDM_LOG_METHOD_SIG() ({})
-#endif
+#endif // DEBUG
+
+NS_ASSUME_NONNULL_END
diff --git a/src/device-manager/cocoa/NLWeaveLogWriter.h b/src/device-manager/cocoa/NLWeaveLogWriter.h
new file mode 100644
index 0000000..0946bc1
--- /dev/null
+++ b/src/device-manager/cocoa/NLWeaveLogWriter.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright (c) 2020 Google LLC
+ * 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
+ * This file defines the @c NLWeaveLogWriter protocol for an external Weave log writer.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import "NLWeaveLogging.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * Defines an interface for an external log writer to register to receive Weave logs.
+ *
+ * External clients can implement this protocol to receive Weave log messages for custom handling –
+ * the shared @c NLWeaveLogWriter can be set using +[NLWeaveLogging setSharedLogWriter:].
+ */
+@protocol NLWeaveLogWriter <NSObject>
+
+/**
+ * Method called with individual Weave log messages as they are logged.
+ *
+ * @param logModule The Weave module in which the log was created.
+ * @param logLevel The logging level of the log.
+ * @param logMessage The formatted log message.
+ */
+- (void)writeLogFromModule:(NLLogModule)logModule
+ level:(NLLogLevel)logLevel
+ message:(NSString *)logMessage NS_SWIFT_NAME(writeLog(from:level:message:));
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/src/device-manager/cocoa/NLWeaveLogging.h b/src/device-manager/cocoa/NLWeaveLogging.h
new file mode 100644
index 0000000..00289c3
--- /dev/null
+++ b/src/device-manager/cocoa/NLWeaveLogging.h
@@ -0,0 +1,128 @@
+/*
+ *
+ * Copyright (c) 2020 Google LLC
+ * 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
+ * This file implements a platform-specific Weave log interface.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@protocol NLWeaveLogWriter;
+
+/**
+ * Weave logging modules – for indicating which component created a log.
+ *
+ * @note
+ * Must align with the order of nl::Weave::Logging::LogModule values.
+ */
+typedef NS_ENUM(NSInteger, NLLogModule) {
+ NLLogModuleNotSpecified = 0,
+
+ NLLogModuleInet,
+ NLLogModuleBle,
+ NLLogModuleMessageLayer,
+ NLLogModuleSecurityManager,
+ NLLogModuleExchangeManager,
+ NLLogModuleTLV,
+ NLLogModuleASN1,
+ NLLogModuleCrypto,
+ NLLogModuleDeviceManager,
+ NLLogModuleAlarm,
+ NLLogModuleBDX,
+ NLLogModuleDataManagement,
+ NLLogModuleDeviceControl,
+ NLLogModuleDeviceDescription,
+ NLLogModuleEcho,
+ NLLogModuleFabricProvisioning,
+ NLLogModuleNetworkProvisioning,
+ NLLogModuleServiceDirectory,
+ NLLogModuleServiceProvisioning,
+ NLLogModuleSoftwareUpdate,
+ NLLogModuleTokenPairing,
+ NLLogModuleHeatLink,
+ NLLogModuleTimeService,
+ NLLogModuleWeaveTunnel,
+ NLLogModuleHeartbeat,
+ NLLogModuleWeaveSystemLayer,
+ NLLogModuleDropcamLegacyPairing,
+ NLLogModuleEventLogging,
+ NLLogModuleSupport,
+
+ // Module for logs originating from the Objective-C @c NLLogging macros.
+ //
+ // Must NOT overlap with the values mapped from nl::Weave::Logging::LogModule.
+ NLLogModuleCocoa = 100,
+};
+
+/**
+ * Logging levels – for indicating the relative importance of a log message.
+ *
+ * @note
+ * Must align with the order of nl::Weave::Logging::LogCategory values.
+ */
+typedef NS_ENUM(NSInteger, NLLogLevel) {
+ NLLogLevelNone = 0,
+ NLLogLevelError,
+ NLLogLevelProgress,
+ NLLogLevelDetail,
+ NLLogLevelRetain,
+};
+
+/**
+ * Platform-specific component for managing Weave log messages.
+ *
+ * Exposes an interface for external clients to configure the shared @c NLWeaveLogWriter – the log
+ * writer will receive Weave logs from both the shared C++ source code and the platform-specific
+ * Objective-C source code. This allows clients to connect Weave logs to their own logging system.
+ */
+@interface NLWeaveLogging : NSObject
+
+#pragma Logging Configuration
+
+/**
+ * Sets the shared @c NLWeaveLogWriter to start receiving Weave logs.
+ *
+ * @note
+ * This should be called prior to any Weave operations to ensure the log writer has been
+ * configured before any logs are written. The log writer will only receive messages logged after
+ * it has been set as the shared writer.
+ */
++ (void)setSharedLogWriter:(nullable id<NLWeaveLogWriter>)logWriter;
+
+#pragma Log Methods
+
+/**
+ * Internal handler method for logging a message to the console and notifying the shared log writer.
+ *
+ * @param logModule The logging module to which the log belongs.
+ * @param logModuleName The name of the logging module.
+ * @param logLevel The level of the log message.
+ * @param formattedLogMessage The formatted log message.
+ */
++ (void)handleWeaveLogFromModule:(NLLogModule)logModule
+ moduleName:(NSString *)logModuleName
+ level:(NLLogLevel)logLevel
+ formattedMessage:(NSString *)formattedLogMessage;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/src/device-manager/cocoa/NLWeaveLogging.mm b/src/device-manager/cocoa/NLWeaveLogging.mm
index 64f1e95..dcfa832 100644
--- a/src/device-manager/cocoa/NLWeaveLogging.mm
+++ b/src/device-manager/cocoa/NLWeaveLogging.mm
@@ -26,6 +26,8 @@
#include <stdio.h>
#import <Foundation/Foundation.h>
+#import "NLWeaveLogWriter.h"
+#import "NLWeaveLogging.h"
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
@@ -34,15 +36,14 @@
#include <Weave/Support/logging/WeaveLogging.h>
+NS_ASSUME_NONNULL_BEGIN
+
#if WEAVE_ERROR_LOGGING || WEAVE_PROGRESS_LOGGING || WEAVE_DETAIL_LOGGING
-#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) \
- ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
+#pragma mark - nl::Weave::Logging::Log
namespace nl {
-
namespace Weave {
-
namespace Logging {
/*
@@ -90,28 +91,170 @@
vsnprintf(formattedMsg + prefixLen, sizeof(formattedMsg) - prefixLen, aMsg, v);
- // For versions >= iOS 10, NSLog is not using Apple System logger anymore
- // So for the logs to always show up in device console, we need to use os_log
- // with OS_LOG_DEFAULT which always gets logged in accordance with system's
- // standard behavior
-#if TARGET_OS_IPHONE
- if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10.0")) {
- os_log(OS_LOG_DEFAULT, "%s", formattedMsg);
- } else {
- NSLog(@"%s", formattedMsg);
- }
-#else
- NSLog(@"%s", formattedMsg);
-#endif
+ // Pass the formatted log to @c NLWeaveLogging for handling.
+ [NLWeaveLogging handleWeaveLogFromModule:(NLLogModule) aModule
+ moduleName:[NSString stringWithUTF8String:moduleName]
+ level:(NLLogLevel) aCategory
+ formattedMessage:[NSString stringWithUTF8String:formattedMsg]];
}
va_end(v);
}
} // namespace Logging
-
} // namespace Weave
-
} // namespace nl
#endif // WEAVE_ERROR_LOGGING || WEAVE_PROGRESS_LOGGING || WEAVE_DETAIL_LOGGING
+
+#if TARGET_OS_IPHONE && DEBUG
+
+/**
+ * @def IS_OPERATING_SYSTEM_AT_LEAST_VERSION(version)
+ *
+ * @brief
+ * Compares the current version of operating system to the given version.
+ *
+ * @param version An @c NSOperatingSystemVersion describing an iOS operating system version.
+ *
+ * @return A boolean indicating whether the operating system version is at least the given version.
+ */
+#define IS_OPERATING_SYSTEM_AT_LEAST_VERSION(version) \
+ ([NSProcessInfo instancesRespondToSelector:@selector(isOperatingSystemAtLeastVersion:)] && \
+ [[NSProcessInfo new] isOperatingSystemAtLeastVersion:version])
+
+/** Minimum iOS version with OSLog support (iOS 10.0.0). */
+static const NSOperatingSystemVersion kMinimumOSLogOperatingSystemVersion
+ = { .majorVersion = 10, .minorVersion = 0, .patchVersion = 0 };
+
+/** Root of the OSLog subsystem for Weave logs. */
+static NSString * const kWeaveOSLogSubsystem = @"com.nest.weave";
+
+/** Name of the subsystem for Weave logs from the shared C++ code. */
+static NSString * const kWeaveCppComponent = @"cpp";
+
+/** Name of the subsystem for Weave logs from the platform-specific Objective-C code. */
+static NSString * const kWeaveCocoaComponent = @"cocoa";
+
+/** Root of the OSLog category for Weave logs. */
+static NSString * const kWeaveOSLogCategory = @"WEAVE";
+
+#pragma mark - @c NLOSLogStore
+
+/** Stores a dictionary mapping each @c NLLogModule to an OSLog logging component. */
+@interface NLOSLogStore : NSObject
+
+/** Returns the @c os_log_t for the given @c NLLogModule, creating a new one if needed. */
++ (os_log_t)logForModule:(NLLogModule)logModule name:(NSString *)moduleName;
+
+@end
+
+@implementation NLOSLogStore
+
+/** Map of @c NLLogModule values to their OS Log object. */
+static NSMutableDictionary<NSNumber *, os_log_t> * gModuleMap;
+
+#pragma mark Initialization
+
++ (void)initialize
+{
+ if (self != [NLOSLogStore class]) {
+ return;
+ }
+
+ @synchronized(self) {
+ gModuleMap = [[NSMutableDictionary alloc] init];
+ }
+}
+
+#pragma mark Public
+
++ (os_log_t)logForModule:(NLLogModule)logModule name:(NSString *)logModuleName
+{
+ @synchronized(self) {
+ if (!gModuleMap[@(logModule)]) {
+ gModuleMap[@(logModule)] = [self createLogForModule:logModule name:logModuleName];
+ }
+
+ return gModuleMap[@(logModule)];
+ }
+}
+
+#pragma mark Private
+
++ (os_log_t)createLogForModule:(NLLogModule)logModule name:(NSString *)name
+{
+ NSString * component = (logModule == NLLogModuleCocoa ? kWeaveCocoaComponent : kWeaveCppComponent);
+ NSString * subsystem = [NSString stringWithFormat:@"%@.%@", kWeaveOSLogSubsystem, component];
+ NSString * category = [NSString stringWithFormat:@"%@.%@", kWeaveOSLogCategory, name];
+ return os_log_create(subsystem.UTF8String, category.UTF8String);
+}
+
+@end
+
+#endif // TARGET_OS_IPHONE && DEBUG
+
+#pragma mark - @c NLWeaveLogging
+
+@implementation NLWeaveLogging
+
+/** The shared external log writer that is subscribed to receive Weave logs. */
+static __nullable id<NLWeaveLogWriter> gSharedWriter = nil;
+
+#pragma mark - Public
+
++ (void)setSharedLogWriter:(nullable id<NLWeaveLogWriter>)logWriter
+{
+ @synchronized(self) {
+ gSharedWriter = logWriter;
+ }
+}
+
++ (void)handleWeaveLogFromModule:(NLLogModule)logModule
+ moduleName:(NSString *)logModuleName
+ level:(NLLogLevel)logLevel
+ formattedMessage:(NSString *)formattedLogMessage
+{
+ if (!formattedLogMessage) {
+ return;
+ }
+
+ // 1. Write the log to the system console (in debug builds).
+#if DEBUG
+ [self writeConsoleLog:formattedLogMessage logModule:logModule logModuleName:logModuleName logLevel:logLevel];
+#endif // DEBUG
+
+ // 2. Notify the shared log writer.
+ id<NLWeaveLogWriter> writer = gSharedWriter;
+ [writer writeLogFromModule:logModule level:logLevel message:formattedLogMessage];
+}
+
+#pragma mark - Private
+
+#if DEBUG
+
+/** Writes a log message to the system console. */
++ (void)writeConsoleLog:(NSString *)formattedLogMessage
+ logModule:(NLLogModule)logModule
+ logModuleName:(NSString *)logModuleName
+ logLevel:(NLLogLevel)logLevel
+{
+#if TARGET_OS_IPHONE
+ // For iOS 10+, OSLog replaces Apple System Logger (and NSLog).
+ if (IS_OPERATING_SYSTEM_AT_LEAST_VERSION(kMinimumOSLogOperatingSystemVersion)) {
+ os_log_t log = [NLOSLogStore logForModule:logModule name:logModuleName];
+ os_log_type_t type = (logLevel == NLLogLevelError ? OS_LOG_TYPE_ERROR : OS_LOG_TYPE_DEFAULT);
+ os_log_with_type(log, type, "%{public}s", formattedLogMessage.UTF8String);
+ return;
+ }
+#endif // TARGET_OS_IPHONE
+
+ // Fallback to NSLog for both iOS <9 and non-iPhone targets.
+ NSLog(@"%@", formattedLogMessage);
+}
+
+#endif // DEBUG
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/src/inet/TCPEndPoint.cpp b/src/inet/TCPEndPoint.cpp
index fdfb6d8..21f498d 100644
--- a/src/inet/TCPEndPoint.cpp
+++ b/src/inet/TCPEndPoint.cpp
@@ -708,12 +708,6 @@
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
- if (mUnsentQueue == NULL)
- {
- mUnsentQueue = data;
- mUnsentOffset = 0;
- }
-
#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
if (!mUserTimeoutTimerRunning)
{
@@ -1199,6 +1193,10 @@
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
+
+#if WEAVE_SYSTEM_CONFIG_USE_LWIP
+ mUnackedLength = 0;
+#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
}
INET_ERROR TCPEndPoint::DriveSending()
@@ -1219,44 +1217,60 @@
uint16_t sendWindowSize = tcp_sndbuf(mTCP);
// If there's data to be sent and the send window is open...
- bool canSend = (mUnsentQueue != NULL && sendWindowSize > 0);
+ bool canSend = (RemainingToSend() > 0 && sendWindowSize > 0);
if (canSend)
{
+ // Find first packet buffer with remaining data to send by skipping
+ // all sent but un-acked data.
+ TCPEndPoint::BufferOffset startOfUnsent = FindStartOfUnsent();
+ const Weave::System::PacketBuffer* currentUnsentBuf = startOfUnsent.buffer;
+ uint16_t unsentOffset = startOfUnsent.offset;
+
// While there's data to be sent and a window to send it in...
do
{
- uint16_t bufDataLen = mUnsentQueue->DataLength();
+ VerifyOrDie(currentUnsentBuf != NULL);
+
+ uint16_t bufDataLen = currentUnsentBuf->DataLength();
// Get a pointer to the start of unsent data within the first buffer on the unsent queue.
- uint8_t *sendData = mUnsentQueue->Start() + mUnsentOffset;
+ const uint8_t *sendData = currentUnsentBuf->Start() + unsentOffset;
// Determine the amount of data to send from the current buffer.
- uint16_t sendLen = bufDataLen - mUnsentOffset;
+ uint16_t sendLen = bufDataLen - unsentOffset;
if (sendLen > sendWindowSize)
sendLen = sendWindowSize;
- // Adjust the unsent data offset by the length of data to be written. If the entire buffer
- // has been sent advance to the next one.
- mUnsentOffset += sendLen;
- if (mUnsentOffset == bufDataLen)
- {
- mUnsentQueue = mUnsentQueue->Next();
- mUnsentOffset = 0;
- }
-
- // Adjust the remaining window size.
- sendWindowSize -= sendLen;
-
- // Determine if there's more data to be sent after this buffer.
- canSend = (mUnsentQueue != NULL && sendWindowSize > 0);
-
// Call LwIP to queue the data to be sent, telling it if there's more data to come.
+ // Data is queued in-place as a reference within the source packet buffer. It is
+ // critical that the underlying packet buffer not be freed until the data
+ // is acknowledged, otherwise retransmissions could use an invalid
+ // backing. Using TCP_WRITE_FLAG_COPY would eliminate this requirement, but overall
+ // requires many more memory allocations which may be problematic when very
+ // memory-constrained or when using pool-based allocations.
lwipErr = tcp_write(mTCP, sendData, sendLen, (canSend) ? TCP_WRITE_FLAG_MORE : 0);
if (lwipErr != ERR_OK)
{
err = Weave::System::MapErrorLwIP(lwipErr);
break;
}
+ // Start accounting for the data sent as yet-to-be-acked.
+ mUnackedLength += sendLen;
+
+ // Adjust the unsent data offset by the length of data that was written.
+ // If the entire buffer has been sent advance to the next one.
+ unsentOffset += sendLen;
+ if (unsentOffset == bufDataLen)
+ {
+ currentUnsentBuf = currentUnsentBuf->Next();
+ unsentOffset = 0;
+ }
+
+ // Adjust the remaining window size.
+ sendWindowSize -= sendLen;
+
+ // Determine if there's more data to be sent after this buffer.
+ canSend = (RemainingToSend() > 0 && sendWindowSize > 0);
} while (canSend);
// Call LwIP to send the queued data.
@@ -1274,7 +1288,7 @@
if (err == INET_NO_ERROR)
{
// If in the SendShutdown state and the unsent queue is now empty, shutdown the PCB for sending.
- if (State == kState_SendShutdown && mUnsentQueue == NULL)
+ if (State == kState_SendShutdown && (RemainingToSend() == 0))
{
lwipErr = tcp_shutdown(mTCP, 0, 1);
if (lwipErr != ERR_OK)
@@ -1571,6 +1585,9 @@
mSendQueue = NULL;
PacketBuffer::Free(mRcvQueue);
mRcvQueue = NULL;
+#if WEAVE_SYSTEM_CONFIG_USE_LWIP
+ mUnackedLength = 0;
+#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
// Call the appropriate app callback if allowed.
if (!suppressCallback)
@@ -1752,6 +1769,67 @@
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
+uint32_t TCPEndPoint::RemainingToSend()
+{
+ if (mSendQueue == NULL)
+ {
+ return 0;
+ }
+ else
+ {
+ // We can never have reported more unacked data than there is pending
+ // in the send queue! This would indicate a critical accounting bug.
+ VerifyOrDie(mUnackedLength <= mSendQueue->TotalLength());
+
+ return mSendQueue->TotalLength() - mUnackedLength;
+ }
+}
+
+TCPEndPoint::BufferOffset TCPEndPoint::FindStartOfUnsent()
+{
+ // Find first packet buffer with remaining data to send by skipping
+ // all sent but un-acked data. This is necessary because of the Consume()
+ // call in HandleDataSent(), which potentially releases backing memory for
+ // fully-sent packet buffers, causing an invalidation of all possible
+ // offsets one might have cached. The TCP acnowledgements may come back
+ // with a variety of sizes depending on prior activity, and size of the
+ // send window. The only way to ensure we get the correct offsets into
+ // unsent data while retaining the buffers that have un-acked data is to
+ // traverse all sent-but-unacked data in the chain to reach the beginning
+ // of ready-to-send data.
+ Weave::System::PacketBuffer* currentUnsentBuf = mSendQueue;
+ uint16_t unsentOffset = 0;
+ uint32_t leftToSkip = mUnackedLength;
+
+ VerifyOrDie(leftToSkip < mSendQueue->TotalLength());
+
+ while (leftToSkip > 0)
+ {
+ VerifyOrDie(currentUnsentBuf != NULL);
+ uint16_t bufDataLen = currentUnsentBuf->DataLength();
+ if (leftToSkip >= bufDataLen)
+ {
+ // We have more to skip than current packet buffer size.
+ // Follow the chain to continue.
+ currentUnsentBuf = currentUnsentBuf->Next();
+ leftToSkip -= bufDataLen;
+ }
+ else
+ {
+ // Done skipping all data, currentUnsentBuf is first packet buffer
+ // containing unsent data.
+ unsentOffset = leftToSkip;
+ leftToSkip = 0;
+ }
+ }
+
+ TCPEndPoint::BufferOffset startOfUnsent;
+ startOfUnsent.buffer = currentUnsentBuf;
+ startOfUnsent.offset = unsentOffset;
+
+ return startOfUnsent;
+}
+
INET_ERROR TCPEndPoint::GetPCB(IPAddressType addrType)
{
// IMMPORTANT: This method MUST be called with the LwIP stack LOCKED!
@@ -1843,8 +1921,24 @@
{
if (IsConnected())
{
+ // Ensure we do not have internal inconsistency in the lwIP, which
+ // could cause invalid pointer accesses.
+ if (lenSent > mUnackedLength)
+ {
+ WeaveLogError(Inet, "Got more ACKed bytes (%d) than were pending (%d)", (int)lenSent, (int)mUnackedLength);
+ DoClose(INET_ERROR_UNEXPECTED_EVENT, false);
+ return;
+ }
+ else if (mSendQueue == NULL)
+ {
+ WeaveLogError(Inet, "Got ACK for %d bytes but data backing gone", (int)lenSent);
+ DoClose(INET_ERROR_UNEXPECTED_EVENT, false);
+ return;
+ }
+
// Consume data off the head of the send queue equal to the amount of data being acknowledged.
mSendQueue = mSendQueue->Consume(lenSent);
+ mUnackedLength -= lenSent;
#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
// Only change the UserTimeout timer if lenSent > 0,
@@ -1852,7 +1946,7 @@
// across.
if (lenSent > 0)
{
- if (mSendQueue == NULL)
+ if (RemainingToSend() == 0)
{
// If the output queue has been flushed then stop the timer.
@@ -1880,12 +1974,12 @@
if (OnDataSent != NULL)
OnDataSent(this, lenSent);
- // If unsent data exists, attempt to sent it now...
- if (mUnsentQueue != NULL)
+ // If unsent data exists, attempt to send it now...
+ if (RemainingToSend() > 0)
DriveSending();
// If in the closing state and the send queue is now empty, attempt to transition to closed.
- if (State == kState_Closing && mSendQueue == NULL)
+ if ((State == kState_Closing) && (RemainingToSend() == 0))
DoClose(INET_NO_ERROR, false);
}
}
diff --git a/src/inet/TCPEndPoint.h b/src/inet/TCPEndPoint.h
index cc4c326..bd570fe 100644
--- a/src/inet/TCPEndPoint.h
+++ b/src/inet/TCPEndPoint.h
@@ -621,9 +621,16 @@
void StopConnectTimer(void);
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
- Weave::System::PacketBuffer *mUnsentQueue;
- uint16_t mUnsentOffset;
+ struct BufferOffset {
+ const Weave::System::PacketBuffer *buffer;
+ uint32_t offset;
+ };
+ uint32_t mUnackedLength; // Amount sent but awaiting ACK. Used as a form of reference count
+ // to hang-on to backing packet buffers until they are no longer needed.
+
+ uint32_t RemainingToSend();
+ BufferOffset FindStartOfUnsent();
INET_ERROR GetPCB(IPAddressType addrType);
void HandleDataSent(uint16_t len);
void HandleDataReceived(Weave::System::PacketBuffer *buf);
diff --git a/src/lib/core/WeaveError.cpp b/src/lib/core/WeaveError.cpp
index 699b50f..875f723 100644
--- a/src/lib/core/WeaveError.cpp
+++ b/src/lib/core/WeaveError.cpp
@@ -232,6 +232,7 @@
case WEAVE_ERROR_SESSION_KEY_SUSPENDED : desc = "Session key suspended"; break;
case WEAVE_ERROR_UNSUPPORTED_WIRELESS_REGULATORY_DOMAIN : desc = "Unsupported wireless regulatory domain"; break;
case WEAVE_ERROR_UNSUPPORTED_WIRELESS_OPERATING_LOCATION : desc = "Unsupported wireless operating location"; break;
+ case WEAVE_ERROR_WDM_EVENT_TOO_BIG : desc = "The WDM Event is too big to be successfully transmitted to a peer node"; break;
}
#endif // !WEAVE_CONFIG_SHORT_ERROR_STR
diff --git a/src/lib/core/WeaveError.h b/src/lib/core/WeaveError.h
index 096294d..2cadb51 100644
--- a/src/lib/core/WeaveError.h
+++ b/src/lib/core/WeaveError.h
@@ -1757,6 +1757,17 @@
*/
#define WEAVE_ERROR_UNSUPPORTED_WIRELESS_OPERATING_LOCATION _WEAVE_ERROR(185)
+/**
+ * @def WEAVE_ERROR_WDM_EVENT_TOO_BIG
+ *
+ * @brief
+ * The specified event is too big to be successfully transmitted to a peer node,
+ * e.g., it exceeds the maximum WDM Notification message size
+ * limit.
+ *
+ */
+#define WEAVE_ERROR_WDM_EVENT_TOO_BIG _WEAVE_ERROR(186)
+
/**
* @}
diff --git a/src/lib/core/WeaveFabricState.cpp b/src/lib/core/WeaveFabricState.cpp
index 4ead562..5a5bfaf 100644
--- a/src/lib/core/WeaveFabricState.cpp
+++ b/src/lib/core/WeaveFabricState.cpp
@@ -300,6 +300,8 @@
sessionEndCallbackList = NULL;
+ BoundConnectionClosedForSession = NULL;
+
State = kState_Initialized;
return WEAVE_NO_ERROR;
diff --git a/src/lib/profiles/data-management/Current/LoggingManagement.cpp b/src/lib/profiles/data-management/Current/LoggingManagement.cpp
index 5ff01af..19b78a2 100644
--- a/src/lib/profiles/data-management/Current/LoggingManagement.cpp
+++ b/src/lib/profiles/data-management/Current/LoggingManagement.cpp
@@ -1314,6 +1314,8 @@
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;
@@ -1329,6 +1331,7 @@
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))
{
@@ -1340,7 +1343,7 @@
GetImportanceBuffer(inSchema.mImportance)->AddEventUTC(opts.timestamp.utcTimestamp);
#if WEAVE_CONFIG_EVENT_LOGGING_VERBOSE_DEBUG_LOGS
- WeaveLogDetail(
+ WeaveLogProgress(
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
@@ -1351,7 +1354,7 @@
GetImportanceBuffer(inSchema.mImportance)->AddEvent(opts.timestamp.systemTimestamp);
#if WEAVE_CONFIG_EVENT_LOGGING_VERBOSE_DEBUG_LOGS
- WeaveLogDetail(
+ WeaveLogProgress(
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
@@ -1943,13 +1946,19 @@
if ((mExchangeMgr != NULL) && (mExchangeMgr->MessageLayer != NULL) && (mExchangeMgr->MessageLayer->SystemLayer != NULL))
{
mExchangeMgr->MessageLayer->SystemLayer->ScheduleWork(LoggingFlushHandler, this);
+ WeaveLogProgress(EventLogging, "Scheduled flush for urgent event.");
}
else
{
err = WEAVE_ERROR_INCORRECT_STATE;
mUploadRequested = false;
+ WeaveLogError(EventLogging, "Schedule flush failed with error: %s", ErrorStr(err));
}
}
+ else if (inRequestFlush)
+ {
+ WeaveLogProgress(EventLogging, "Flush already scheduled, no need to schedule an additional flush.");
+ }
return err;
}
diff --git a/src/lib/profiles/data-management/Current/NotificationEngine.h b/src/lib/profiles/data-management/Current/NotificationEngine.h
index 792b89a..22cfad7 100644
--- a/src/lib/profiles/data-management/Current/NotificationEngine.h
+++ b/src/lib/profiles/data-management/Current/NotificationEngine.h
@@ -33,6 +33,13 @@
#include <Weave/Profiles/data-management/TraitData.h>
#include <Weave/Profiles/data-management/TraitCatalog.h>
+// Reserve bytes that would account for additional formulation overhead
+// of a Notify Request(beyond the event and the data lists) containing
+// meta information, e.g., SubscriptionId, etc. This is a conservative
+// estimate that should serve as a loose upper bound, and help in bounding
+// the maximum size of a WDM event.
+#define WDM_NOTIFY_REQUEST_META_INFO_BYTES_MAX (64)
+
namespace nl {
namespace Weave {
namespace Profiles {
diff --git a/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.cpp b/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.cpp
index 3f1f554..e9375d3 100644
--- a/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.cpp
+++ b/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.cpp
@@ -365,6 +365,18 @@
return mTunAgentState;
}
+/**
+ * Get a pointer to the Weave Tunnel Connection.
+ *
+ * @return WeaveConnection pointer for an established Weave Tunnel. If tunnel is
+ * not in the established state, a null pointer is returned.
+ *
+ */
+nl::Weave::WeaveConnection * WeaveTunnelAgent::GetPrimaryTunnelConnection(void) const
+{
+ return mPrimaryTunConnMgr.GetTunnelConnection();
+}
+
#if WEAVE_CONFIG_PERSIST_CONNECTED_SESSION
void WeaveTunnelAgent::SetCallbacksForPersistedTunnelConnection(WeaveTunnelConnectionMgr::PersistedSecureSessionExistsFunct aIsPersistedTunnelSessionPresent,
WeaveTunnelConnectionMgr::LoadPersistedSessionFunct aLoadPersistedTunnelSession)
diff --git a/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.h b/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.h
index a252005..2188729 100644
--- a/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.h
+++ b/src/lib/profiles/weave-tunneling/WeaveTunnelAgent.h
@@ -216,6 +216,12 @@
*/
AgentState GetWeaveTunnelAgentState(void);
+/**
+ * Get the Primary Tunnel Connection
+ *
+ */
+ nl::Weave::WeaveConnection * GetPrimaryTunnelConnection(void) const;
+
#if WEAVE_CONFIG_TUNNEL_TCP_USER_TIMEOUT_SUPPORTED
/**
* Configure the TCP user timeout option on the primary tunnel connection.
diff --git a/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.cpp b/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.cpp
index 7e4c5a5..ab137c2 100644
--- a/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.cpp
+++ b/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.cpp
@@ -650,6 +650,27 @@
}
/**
+ * Get a pointer to the Weave Tunnel Connection object for this
+ * Tunnel Connection Manager.
+ *
+ * @return pointer to the WeaveConnection object for the Tunnel. If Tunnel is
+ * not in an established state, a null pointer is returned
+ *
+ */
+
+nl::Weave::WeaveConnection * WeaveTunnelConnectionMgr::GetTunnelConnection(void) const
+{
+ if (mConnectionState == kState_ConnectionEstablished || mConnectionState == kState_TunnelOpen)
+ {
+ return mServiceCon;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+/**
* Handler to receive tunneled IPv6 packets from the Service TCP connection and forward to the Tunnel
* EndPoint interface after decapsulating the raw IPv6 packet from inside the tunnel header.
*
diff --git a/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.h b/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.h
index aac22c0..aee2a96 100644
--- a/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.h
+++ b/src/lib/profiles/weave-tunneling/WeaveTunnelConnectionMgr.h
@@ -241,6 +241,11 @@
*/
void StopAndReconnectTunnelConn(ReconnectParam & reconnParam);
+/**
+ * Get a pointer to the Weave Tunnel Connection object for this
+ * Tunnel Connection Manager.
+ */
+ nl::Weave::WeaveConnection * GetTunnelConnection(void) const;
private:
WEAVE_ERROR StartServiceTunnelConn(uint64_t destNodeId, IPAddress destIPAddr,
diff --git a/src/test-apps/TestErrorStr.cpp b/src/test-apps/TestErrorStr.cpp
index 6716cbf..51655db 100644
--- a/src/test-apps/TestErrorStr.cpp
+++ b/src/test-apps/TestErrorStr.cpp
@@ -294,6 +294,7 @@
WEAVE_ERROR_SESSION_KEY_SUSPENDED,
WEAVE_ERROR_UNSUPPORTED_WIRELESS_REGULATORY_DOMAIN,
WEAVE_ERROR_UNSUPPORTED_WIRELESS_OPERATING_LOCATION,
+ WEAVE_ERROR_WDM_EVENT_TOO_BIG,
WEAVE_ERROR_TUNNEL_ROUTING_RESTRICTED,
diff --git a/src/test-apps/happy/bin/weave-state.py b/src/test-apps/happy/bin/weave-state.py
index 7f6537c..c33845a 100755
--- a/src/test-apps/happy/bin/weave-state.py
+++ b/src/test-apps/happy/bin/weave-state.py
@@ -40,7 +40,7 @@
["help", "quiet"])
except getopt.GetoptError as err:
- print(WeaveState.WeaveState.__doc__)
+ print(WeaveState.WeaveState.__doc__)
print(hred(str(err)))
sys.exit(hred("%s: Failed to parse arguments." % (__file__)))