Skip to content

Commit d5106c0

Browse files
Add metrics for OTA transfers (project-chip#39991)
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
1 parent 58a69c5 commit d5106c0

10 files changed

Lines changed: 215 additions & 20 deletions

File tree

examples/darwin-framework-tool/commands/provider/OTAProviderDelegate.mm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ - (void)handleBDXTransferSessionBeginForNodeID:(NSNumber * _Nonnull)nodeID
157157

158158
- (void)handleBDXTransferSessionEndForNodeID:(NSNumber * _Nonnull)nodeID
159159
controller:(MTRDeviceController * _Nonnull)controller
160+
metrics:(MTRMetrics * _Nonnull)metrics
160161
error:(NSError * _Nullable)error
161162
{
162163
NSLog(@"BDX TransferSession end with error: %@", error);

src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@
160160
if (strongDelegate && mQueue && strongController) {
161161

162162
// Always collect the metrics to avoid unbounded growth of the stats in the collector
163-
MTRMetrics * metrics = [[MTRMetricsCollector sharedInstance] metricSnapshot:TRUE];
163+
MTRMetrics * metrics = [[MTRMetricsCollector sharedInstance] metricSnapshotForCommissioning:YES];
164164
MTR_LOG("%@ Device commissioning complete with metrics %@", strongController, metrics);
165165

166166
if ([strongDelegate respondsToSelector:@selector(controller:commissioningComplete:nodeID:)] ||

src/darwin/Framework/CHIP/MTRMetricKeys.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ namespace chip {
2222
namespace Tracing {
2323
namespace DarwinFramework {
2424

25+
// When metrics were originally added, they were used for logging events during the commissioning of devices. The data was sent at
26+
// the end of commissioning (in OnCommissioningComplete). This means that if we want to add metrics that are not related to
27+
// commissioning, that need to be sent outside of the commissioning of the device, we need a way to separate out metrics by a
28+
// category. The CHIP metrics infrastructure is fairly simple, with just a key and a numeric value. To add categories without
29+
// changing existing keys we've opted to use a special prefix with double underscores (dwnfw__CATEGORY__) for the keys that are in a
30+
// category. Any keys that do not contain this special prefix are now assumed to be used for commissioning events.
31+
32+
// Note that these are not used for commissioning metrics, because those predate the encoding of a category.
33+
#define METRICS_KEY_PREFIX "dwnfw__"
34+
#define METRICS_KEY(category, key) METRICS_KEY_PREFIX #category "__" #key
35+
2536
// Tracks overall commissioning via one of the setup APIs
2637
constexpr Tracing::MetricKey kMetricDeviceCommissioning = "dwnfw_device_commissioning";
2738

@@ -94,6 +105,23 @@ constexpr Tracing::MetricKey kMetricUnexpectedCQualityUpdate = "dwnpm_bad_c_attr
94105
// Setup from darwin MTRDevice for initial subscription to a device
95106
constexpr Tracing::MetricKey kMetricMTRDeviceInitialSubscriptionSetup = "dwnpm_dev_initial_subscription_setup";
96107

108+
constexpr Tracing::MetricKey kMetricOTATransfer = METRICS_KEY(ota, transfer);
109+
110+
// Device Vendor ID
111+
constexpr Tracing::MetricKey kMetricOTADeviceVendorID = METRICS_KEY(ota, device_vendor_id);
112+
113+
// Device Product ID
114+
constexpr Tracing::MetricKey kMetricOTADeviceProductID = METRICS_KEY(ota, device_product_id);
115+
116+
// Device Uses Thread
117+
constexpr Tracing::MetricKey kMetricOTADeviceUsesThread = METRICS_KEY(ota, device_uses_thread_bool);
118+
119+
constexpr Tracing::MetricKey kMetricOTATransferLength = METRICS_KEY(ota, transfer_length);
120+
121+
constexpr Tracing::MetricKey kMetricOTATransferOffset = METRICS_KEY(ota, transfer_offset);
122+
123+
constexpr Tracing::MetricKey kMetricOTATNumBytesProcessed = METRICS_KEY(ota, num_bytes_processed);
124+
97125
} // namespace DarwinFramework
98126
} // namespace Tracing
99127
} // namespace chip

src/darwin/Framework/CHIP/MTRMetricsCollector.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,26 @@ void ShutdownMetricsCollection();
4444
+ (instancetype)sharedInstance;
4545

4646
/**
47-
* @brief This method creates a snapshot of the metrics collected until the current point in time
48-
* and returns an object with the stats.
47+
* @brief This method creates a snapshot of the metrics for commissioning collected until the current
48+
* point in time and returns an object with the stats.
4949
*
5050
* @param [in] resetCollection Boolean that specifies whether or not to clear the stats collected after
5151
* creating the snapshot.
5252
*
5353
* @return MTRMetric object representing the metric data.
5454
*/
55-
- (MTRMetrics *)metricSnapshot:(BOOL)resetCollection;
55+
- (MTRMetrics *)metricSnapshotForCommissioning:(BOOL)resetCollection;
56+
57+
/**
58+
* @brief This method creates a snapshot of the metrics for a category (other than for commissioning)
59+
* that were collected until the current point in time and returns an object with those stats.
60+
*
61+
* @param [in] removeMetrics Boolean that specifies whether to clear the stats for the category after
62+
* creating the snapshot.
63+
*
64+
* @return MTRMetric object representing the metric data.
65+
*/
66+
- (MTRMetrics *)metricSnapshotForCategory:(NSString *)category removeMetrics:(BOOL)removeMetrics;
5667

5768
/**
5869
* @brief This method clears any metrics collected.

src/darwin/Framework/CHIP/MTRMetricsCollector.mm

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#import "MTRMetricsCollector.h"
1919
#import "MTRLogging_Internal.h"
20+
#import "MTRMetricKeys.h"
2021
#import "MTRMetrics.h"
2122
#import "MTRMetrics_Internal.h"
2223
#import <MTRUnfairLock.h>
@@ -251,18 +252,45 @@ - (void)handleMetricEvent:(MetricEvent)event
251252
}
252253
}
253254

254-
- (MTRMetrics *)metricSnapshot:(BOOL)resetCollection
255+
- (MTRMetrics *)metricSnapshotForCommissioning:(BOOL)resetCollection
255256
{
256257
std::lock_guard lock(_lock);
257258

259+
NSMutableArray * keysToDelete = [NSMutableArray array];
258260
MTRMetrics * metrics = [[MTRMetrics alloc] initWithCapacity:[_metricsDataCollection count]];
259-
for (NSString * key in _metricsDataCollection) {
260-
[metrics setMetricData:_metricsDataCollection[key] forKey:key];
261-
}
261+
[_metricsDataCollection enumerateKeysAndObjectsUsingBlock:^(NSString * key, MTRMetricData * obj, BOOL * stop) {
262+
// Commissioning metric keys predate the encoding of a category, so we need to filter out
263+
// all keys that use the encoding scheme here.
264+
if (![key hasPrefix:@METRICS_KEY_PREFIX]) {
265+
[keysToDelete addObject:key];
266+
[metrics setMetricData:obj forKey:key];
267+
}
268+
}];
262269

263270
// Clear curent stats, if specified
264271
if (resetCollection) {
265-
[_metricsDataCollection removeAllObjects];
272+
[_metricsDataCollection removeObjectsForKeys:keysToDelete];
273+
}
274+
return metrics;
275+
}
276+
277+
- (MTRMetrics *)metricSnapshotForCategory:(NSString *)category removeMetrics:(BOOL)removeMetrics
278+
{
279+
std::lock_guard lock(_lock);
280+
281+
NSString * keyPrefix = [NSString stringWithFormat:@METRICS_KEY_PREFIX "%@__", category];
282+
NSMutableArray * keysToDelete = [NSMutableArray array];
283+
MTRMetrics * metrics = [[MTRMetrics alloc] initWithCapacity:[_metricsDataCollection count]];
284+
[_metricsDataCollection enumerateKeysAndObjectsUsingBlock:^(NSString * key, MTRMetricData * obj, BOOL * stop) {
285+
if ([key hasPrefix:keyPrefix]) {
286+
[keysToDelete addObject:key];
287+
[metrics setMetricData:obj forKey:key];
288+
}
289+
}];
290+
291+
// Clear curent stats, if specified
292+
if (removeMetrics) {
293+
[_metricsDataCollection removeObjectsForKeys:keysToDelete];
266294
}
267295
return metrics;
268296
}

src/darwin/Framework/CHIP/MTROTAImageTransferHandler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ class MTROTAImageTransferHandler : public chip::bdx::AsyncResponder {
7878
bool mIsPeerNodeAKnownThreadDevice = NO;
7979

8080
chip::System::Clock::Milliseconds32 mBDXThrottleIntervalForThreadDevices;
81+
82+
size_t mNumBytesProcessed = 0;
8183
};
8284

8385
NS_ASSUME_NONNULL_END

src/darwin/Framework/CHIP/MTROTAImageTransferHandler.mm

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#import "MTRDeviceControllerFactory_Internal.h"
2020
#import "MTRDeviceController_Internal.h"
2121
#import "MTRError_Internal.h"
22+
#import "MTRMetricKeys.h"
23+
#import "MTRMetricsCollector.h"
2224
#import "MTROTAUnsolicitedBDXMessageHandler.h"
2325
#import "NSStringSpanConversion.h"
2426

@@ -28,6 +30,7 @@
2830
using namespace chip;
2931
using namespace chip::bdx;
3032
using namespace chip::app;
33+
using namespace chip::Tracing::DarwinFramework;
3134

3235
constexpr uint16_t kMaxBdxBlockSize = 1024;
3336

@@ -199,6 +202,11 @@ - (void)SetOtaImageTransferHandler:(MTROTAImageTransferHandler *)otaImageTransfe
199202
auto * controller = [[MTRDeviceControllerFactory sharedInstance] runningControllerForFabricIndex:mPeer.GetFabricIndex()];
200203
VerifyOrReturnError(controller != nil, CHIP_ERROR_INCORRECT_STATE);
201204

205+
mNumBytesProcessed = 0;
206+
207+
MATTER_LOG_METRIC_BEGIN(kMetricOTATransfer);
208+
MATTER_LOG_METRIC(kMetricOTATransferOffset, uint32_t(mTransfer.GetStartOffset()));
209+
202210
MTROTAImageTransferHandlerWrapper * __weak weakWrapper = mOTAImageTransferHandlerWrapper;
203211

204212
auto completionHandler = ^(NSError * _Nullable error) {
@@ -284,8 +292,24 @@ - (void)SetOtaImageTransferHandler:(MTROTAImageTransferHandler *)otaImageTransfe
284292
return;
285293
}
286294

295+
auto * device = [MTRDevice deviceWithNodeID:nodeId controller:controller];
296+
297+
MATTER_LOG_METRIC(kMetricOTADeviceVendorID, device.vendorID.unsignedIntValue);
298+
MATTER_LOG_METRIC(kMetricOTADeviceProductID, device.productID.unsignedIntValue);
299+
MATTER_LOG_METRIC(kMetricOTADeviceUsesThread, mIsPeerNodeAKnownThreadDevice);
300+
MATTER_LOG_METRIC(kMetricOTATNumBytesProcessed, uint32_t(mNumBytesProcessed));
301+
MATTER_LOG_METRIC_END(kMetricOTATransfer, error);
302+
303+
// Always collect the metrics to avoid unbounded growth of the stats in the collector
304+
MTRMetrics * metrics = [[MTRMetricsCollector sharedInstance] metricSnapshotForCategory:@("ota") removeMetrics:YES];
287305
auto nsError = [MTRError errorForCHIPErrorCode:error];
288-
if ([strongDelegate respondsToSelector:@selector(handleBDXTransferSessionEndForNodeID:controller:error:)]) {
306+
if ([strongDelegate respondsToSelector:@selector(handleBDXTransferSessionEndForNodeID:controller:metrics:error:)]) {
307+
dispatch_async(delegateQueue, ^{
308+
[strongDelegate handleBDXTransferSessionEndForNodeID:nodeId controller:controller
309+
metrics:metrics
310+
error:nsError];
311+
});
312+
} else if ([strongDelegate respondsToSelector:@selector(handleBDXTransferSessionEndForNodeID:controller:error:)]) {
289313
dispatch_async(delegateQueue, ^{
290314
[strongDelegate handleBDXTransferSessionEndForNodeID:nodeId
291315
controller:controller
@@ -330,6 +354,9 @@ - (void)SetOtaImageTransferHandler:(MTROTAImageTransferHandler *)otaImageTransfe
330354
MTROTAImageTransferHandlerWrapper * __weak weakWrapper = mOTAImageTransferHandlerWrapper;
331355

332356
auto respondWithBlock = ^(NSData * _Nullable data, BOOL isEOF) {
357+
if (data) {
358+
mNumBytesProcessed += data.length;
359+
}
333360
[controller
334361
asyncDispatchToMatterQueue:^() {
335362
assertChipStackLockedByCurrentThread();

src/darwin/Framework/CHIP/MTROTAProviderDelegate.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#import <Matter/MTRDefines.h>
2222

2323
@class MTRDeviceController;
24+
@class MTRMetrics;
2425

2526
NS_ASSUME_NONNULL_BEGIN
2627

@@ -140,7 +141,15 @@ NS_ASSUME_NONNULL_BEGIN
140141
*/
141142
- (void)handleBDXTransferSessionEndForNodeID:(NSNumber *)nodeID
142143
controller:(MTRDeviceController *)controller
143-
error:(NSError * _Nullable)error;
144+
metrics:(MTRMetrics *)metrics
145+
error:(NSError * _Nullable)error
146+
MTR_AVAILABLE(ios(26.1), macos(26.1), watchos(26.1), tvos(26.1));
147+
148+
- (void)handleBDXTransferSessionEndForNodeID:(NSNumber *)nodeID
149+
controller:(MTRDeviceController *)controller
150+
error:(NSError * _Nullable)error
151+
MTR_DEPRECATED_WITH_REPLACEMENT("handleBDXTransferSessionEndForNodeID:controller:metrics:error:", ios(16.1, 26.1),
152+
macos(13.0, 26.1), watchos(9.1, 26.1), tvos(16.1, 26.1));
144153

145154
/**
146155
* Notify the delegate when a BDX Query message has been received for some node.

0 commit comments

Comments
 (0)