From b4176e09be897b7fa013c73123dfeb7a0ea20ddc Mon Sep 17 00:00:00 2001 From: Pete Markowsky Date: Wed, 28 Jul 2021 15:55:46 -0400 Subject: [PATCH 1/6] Added types for recording and storing performance metrics. This adds SNTMetricSet and various gauge and counter types to allow for exporting metrics such as CPU,Memory usage and other properties that are useful for tracking reliability and debugging. This is the first commit of a series to add support for monitoring systems. --- Santa.xcodeproj/project.pbxproj | 10 + Source/common/BUILD | 14 +- Source/common/SNTMetricSet.h | 175 +++++++++ Source/common/SNTMetricSet.m | 589 +++++++++++++++++++++++++++++++ Source/common/SNTMetricSetTest.m | 527 +++++++++++++++++++++++++++ 5 files changed, 1314 insertions(+), 1 deletion(-) create mode 100644 Source/common/SNTMetricSet.h create mode 100644 Source/common/SNTMetricSet.m create mode 100644 Source/common/SNTMetricSetTest.m diff --git a/Santa.xcodeproj/project.pbxproj b/Santa.xcodeproj/project.pbxproj index c1881ea2f..14813cb05 100644 --- a/Santa.xcodeproj/project.pbxproj +++ b/Santa.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 0D9F577C2342650F005D9AA8 /* SNTPrefixTree.cc in Sources */ = {isa = PBXBuildFile; fileRef = C7658B022322B84F00F36578 /* SNTPrefixTree.cc */; }; + 4D3AD70026B49EB400110D20 /* SNTMetricSetTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D3AD6FE26B49EB400110D20 /* SNTMetricSetTest.m */; }; + 4D3AD70126B49EB400110D20 /* SNTMetricSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D3AD6FF26B49EB400110D20 /* SNTMetricSet.m */; }; 59502195B2982225D3706DCE /* libPods-santabundleservice.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A55D73A235850B9FA991865 /* libPods-santabundleservice.a */; }; AD3736AF78C41A962C26D429 /* libPods-Santa.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C3E743944A9D77423AA1534 /* libPods-Santa.a */; }; B617F8907035AD3DA2CB5C45 /* libPods-syncservice.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D10E53C5ACA15304D9A7CC95 /* libPods-syncservice.a */; }; @@ -211,6 +213,9 @@ 2A55D73A235850B9FA991865 /* libPods-santabundleservice.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-santabundleservice.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 4A98E2E9727FBEF27CDDA298 /* Pods-syncservice.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-syncservice.debug.xcconfig"; path = "Target Support Files/Pods-syncservice/Pods-syncservice.debug.xcconfig"; sourceTree = ""; }; 4C3E743944A9D77423AA1534 /* libPods-Santa.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Santa.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4D3AD6FD26B49EB400110D20 /* SNTMetricSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTMetricSet.h; sourceTree = ""; }; + 4D3AD6FE26B49EB400110D20 /* SNTMetricSetTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTMetricSetTest.m; sourceTree = ""; }; + 4D3AD6FF26B49EB400110D20 /* SNTMetricSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTMetricSet.m; sourceTree = ""; }; 4E28DBA012524ABF55F8300C /* Pods-com.google.santa.daemon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-com.google.santa.daemon.debug.xcconfig"; path = "Target Support Files/Pods-com.google.santa.daemon/Pods-com.google.santa.daemon.debug.xcconfig"; sourceTree = ""; }; 7AF15DF785BAA0EAB0BE340D /* Pods-santad.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santad.release.xcconfig"; path = "Target Support Files/Pods-santad/Pods-santad.release.xcconfig"; sourceTree = ""; }; 7FEBB0D42E35B93E5B995B06 /* Pods-syncservice.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-syncservice.release.xcconfig"; path = "Target Support Files/Pods-syncservice/Pods-syncservice.release.xcconfig"; sourceTree = ""; }; @@ -636,6 +641,9 @@ C7658AB02322B84F00F36578 /* common */ = { isa = PBXGroup; children = ( + 4D3AD6FD26B49EB400110D20 /* SNTMetricSet.h */, + 4D3AD6FF26B49EB400110D20 /* SNTMetricSet.m */, + 4D3AD6FE26B49EB400110D20 /* SNTMetricSetTest.m */, C7658AD52322B84F00F36578 /* BUILD */, C7658AD22322B84F00F36578 /* SNTBlockMessage.h */, C7658AC72322B84F00F36578 /* SNTBlockMessage.m */, @@ -1270,6 +1278,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4D3AD70126B49EB400110D20 /* SNTMetricSet.m in Sources */, C7658B192322BDEA00F36578 /* SNTAppDelegate.m in Sources */, C7658B432322C12100F36578 /* SNTRule.m in Sources */, C7658B3D2322C0F500F36578 /* SNTXPCBundleServiceInterface.m in Sources */, @@ -1282,6 +1291,7 @@ C7658B172322BDE500F36578 /* SNTAboutWindowController.m in Sources */, C7658B402322C10700F36578 /* SNTLogging.m in Sources */, C7658B3E2322C0FC00F36578 /* SNTXPCControlInterface.m in Sources */, + 4D3AD70026B49EB400110D20 /* SNTMetricSetTest.m in Sources */, C7658B3C2322C0ED00F36578 /* SNTConfigurator.m in Sources */, C7658B3F2322C10200F36578 /* SNTXPCNotifierInterface.m in Sources */, C7658B442322C12600F36578 /* SNTSystemInfo.m in Sources */, diff --git a/Source/common/BUILD b/Source/common/BUILD index 6d0997a66..0bf40c88a 100644 --- a/Source/common/BUILD +++ b/Source/common/BUILD @@ -188,6 +188,12 @@ objc_library( ], ) +objc_library( + name = "SNTMetricSet", + srcs = ["SNTMetricSet.m"], + hdrs = ["SNTMetricSet.h"], +) + objc_library( name = "SNTXPCSyncdInterface", srcs = ["SNTXPCSyncdInterface.m"], @@ -241,5 +247,11 @@ santa_unit_test( santa_unit_test( name = "SNTPrefixTreeTest", srcs = ["SNTPrefixTreeTest.mm"], - deps = ["SNTPrefixTree"], + deps = [":SNTPrefixTree"], +) + +santa_unit_test( + name = "SNTMetricSetTest", + srcs = ["SNTMetricSetTest.m"], + deps = [":SNTMetricSet"], ) diff --git a/Source/common/SNTMetricSet.h b/Source/common/SNTMetricSet.h new file mode 100644 index 000000000..27e0e9a38 --- /dev/null +++ b/Source/common/SNTMetricSet.h @@ -0,0 +1,175 @@ +/// Copyright 2021 Google 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. + +#import + +/** + * Provides an abstraction for various metric systems that will be exported to + * monitoring systems via the MetricService. This is used store internal + * counters and metrics that can be exported to an external monitoring system. + * + * `SNTMetricSet` for storing and creating metrics and counters. This is + * the externally visible interface + * class. + * + * Metric classes: + * * `SNTMetric` to store metric values broken down by "field" dimensions. + * * subclasses of `SNTMetric` with suitable setters: + * * `SNTMetricCounter` + * * `SNTMetricGaugeInt64` + * * `SNTMetricGaugeDouble` + * * `SNTMetricString` + * * `SNTMetricBoolean` + */ + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, SNTMetricType) { + SNTMetricTypeUnknown = 0, + SNTMetricTypeConstantBool = 1, + SNTMetricTypeConstantString = 2, + SNTMetricTypeConstantInt64 = 3, + SNTMetricTypeConstantDouble = 4, + SNTMetricTypeBoolGauge = 5, + SNTMetricTypeStringGauge = 6, + SNTMetricTypeInt64Gauge = 7, + SNTMetricTypeDoubleGauge = 8, + SNTMetricTypeCounter = 9, +}; + +/* + */ +@interface SNTMetric : NSObject +- (NSDictionary *)exportNSDictionary; +@end + +@interface SNTMetricCounter : SNTMetric +- (void)incrementBy:(long long)step forFieldValues:(NSArray *)fieldValues; +- (void)incrementForFieldValues:(NSArray *)fieldValues; +- (long long)getCountForFieldValues:(NSArray *)fieldValues; +@end + +@interface SNTMetricInt64Gauge : SNTMetric +- (void)set:(long long)value forFieldValues:(NSArray *)fieldValues; +- (long long)getGaugeValueForFieldValues:(NSArray *)fieldValues; +@end + +@interface SNTMetricDoubleGauge : SNTMetric +- (void)set:(double)value forFieldValues:(NSArray *)fieldValues; +- (double)getGaugeValueForFieldValues:(NSArray *)fieldValues; +@end + +@interface SNTMetricStringGauge : SNTMetric +- (void)set:(NSString *)value forFieldValues:(NSArray *)fieldValues; +- (NSString *)getStringValueForFieldValues:(NSArray *)fieldValues; +@end + +@interface SNTMetricBooleanGauge : SNTMetric +- (void)set:(BOOL)value forFieldValues:(NSArray *)fieldValues; +- (BOOL)getBoolValueForFieldValues:(NSArray *)fieldValues; +@end + +/** + * A registry of metrics with associated fields. + */ +@interface SNTMetricSet : NSObject +- (instancetype)initWithHostname:(NSString *)hostname username:(NSString *)username; + +/* Returns a counter with the given name, field names and help + * text, registered with the MetricSet. + * + * @param name The counter name, for example @"/proc/cpu". + * @param fieldNames The counter's field names, for example @[@"result"]. + * @param helpText The counter's help description. + * @return A counter with the given specification registered with this root. + * The returned counter might have been created earlier with the same + * specification. + * @throw NSInternalInconsistencyException When trying to register a second + * counter with the same name but a different schema as an existing one + */ +- (SNTMetricCounter *)counterWithName:(NSString *)name + fieldNames:(NSArray *)fieldNames + helpText:(NSString *)text; + +- (void)addRootLabel:(NSString *)label value:(NSString *)value; + +/** + * Returns a int64 gauge metric with the given Streamz name and help text, + * registered with this MetricSet. + * + * @param name The metric name, for example @"/memory/free". + * @param fieldNames The metric's field names, for example @[@"type"]. + * @param helpText The metric's help description. + */ +- (SNTMetricInt64Gauge *)int64GaugeWithName:(NSString *)name + fieldNames:(NSArray *)fieldNames + helpText:(NSString *)helpText; + +/** + * Returns a double gauge metric with the given name and help text, + * registered with this root. + * + * @param name The metric name, for example @"/memory/free". + * @param fieldNames The metric's field names, for example @[@"type"]. + * @param helpText The metric's help description. + */ +- (SNTMetricDoubleGauge *)doubleGaugeWithName:(NSString *)name + fieldNames:(NSArray *)fieldNames + helpText:(NSString *)helpText; + +/** + * Returns a string gauge metric with the given name and help text, + * registered with this metric set. + * + * @param name The metric name, for example @"/santa/mode". + * @param fieldNames The metric's field names, for example @[@"type"]. + * @param helpText The metric's help description. + */ +- (SNTMetricStringGauge *)stringGaugeWithName:(NSString *)name + fieldNames:(NSArray *)fieldNames + helpText:(NSString *)helpText; + +/** + * Returns a boolean gauge metric with the given name and help text, + * registered with this metric set. + * + * @param name The metric name, for example @"/memory/free". + * @param fieldNames The metric's field names, for example @[@"type"]. + * @param helpText The metric's help description. + */ +- (SNTMetricBooleanGauge *)booleanGaugeWithName:(NSString *)name + fieldNames:(NSArray *)fieldNames + helpText:(NSString *)helpText; + +/** Creates a constant metric with a string value and no fields. */ +- (void)addConstantStringWithName:(NSString *)name + helpText:(NSString *)helpText + value:(NSString *)value; + +/** Creates a constant metric with an integer value and no fields. */ +- (void)addConstantIntegerWithName:(NSString *)name + helpText:(NSString *)helpText + value:(long long)value; + +/** Creates a constant metric with an integer value and no fields. */ +- (void)addConstantBooleanWithName:(NSString *)name helpText:(NSString *)helpText value:(BOOL)value; + +/** Register a callback to get executed just before each export. */ +- (void)registerCallback:(void (^)(void))callback; + +/** Export creates an NSDictionary of the state of the metrics */ +- (NSDictionary *)exportNSDictionary; +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/common/SNTMetricSet.m b/Source/common/SNTMetricSet.m new file mode 100644 index 000000000..cae825ceb --- /dev/null +++ b/Source/common/SNTMetricSet.m @@ -0,0 +1,589 @@ +#import "SNTMetricSet.h" + +/** + * SNTMetricValue encapsulates the value of a metric along with the creation + * and update timestamps. It is thread-safe and has a separate field for each + * metric type. + * + * It is intended to only be used by SNTMetrics; + */ +@interface SNTMetricValue : NSObject +/** Increment the counter by the step value, updating timestamps appropriately. */ +- (void)addInt64:(long long)step; + +/** Set the Int64 value. */ +- (void)setInt64:(long long)value; + +/** Set the double value. */ +- (void)setDouble:(double)value; + +/** Set the string value. */ +- (void)setString:(NSString *)value; + +/** Set the BOOL string value. */ +- (void)setBool:(BOOL)value; + +/** + * Clears the last update timestamp. + * + * This makes the metric value always emit the current timestamp as last update timestamp. + */ +- (void)clearLastUpdateTimestamp; + +/** Getters */ +- (long long)getInt64Value; +- (double)getDoubleValue; +- (NSString *)getStringValue; +@end + +@implementation SNTMetricValue { + /** The int64 value for the SNTMetricValue, if set. */ + long long _int64Value; + + /** The double value for the SNTMetricValue, if set. */ + double _doubleValue; + + /** The string value for the SNTMetricValue, if set. */ + NSString *_stringValue; + + /** The boolean value for the SNTMetricValue, if set. */ + BOOL _boolValue; + + /** The first time this cell got created in the current process. */ + NSDate *_creationTime; + + /** The last time that the counter value was changed. */ + NSDate *_lastUpdate; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _int64Value = 0; + _doubleValue = 0; + _stringValue = nil; + _boolValue = NO; + _creationTime = [NSDate date]; + _lastUpdate = _creationTime; + } + return self; +} + +- (void)addInt64:(long long)step { + @synchronized(self) { + _int64Value += step; + _lastUpdate = [NSDate date]; + } +} + +- (void)setInt64:(long long)value { + @synchronized(self) { + _int64Value = value; + _lastUpdate = [NSDate date]; + } +} + +- (long long)getInt64Value { + @synchronized(self) { + return _int64Value; + } +} + +- (void)setDouble:(double)value { + @synchronized(self) { + _doubleValue = value; + _lastUpdate = [NSDate date]; + } +} + +- (double)getDoubleValue { + @synchronized(self) { + return _doubleValue; + } +} + +- (void)setString:(NSString *)value { + @synchronized(self) { + _stringValue = [value copy]; + _lastUpdate = [NSDate date]; + } +} + +- (NSString *)getStringValue { + @synchronized(self) { + return [_stringValue copy]; + } +} + +- (void)setBool:(BOOL)value { + @synchronized(self) { + _boolValue = value; + _lastUpdate = [NSDate date]; + } +} + +- (BOOL)getBoolValue { + @synchronized(self) { + return _boolValue; + } +} + +- (void)clearLastUpdateTimestamp { + @synchronized(self) { + _lastUpdate = nil; + } +} + +- (NSDate *)getLastUpdatedTimestamp { + NSDate *updated = nil; + @synchronized(self) { + updated = [_lastUpdate copy]; + } + return updated; +} + +- (NSDate *)getCreatedTimestamp { + NSDate *created = nil; + @synchronized(self) { + created = [_creationTime copy]; + } + return created; +} +@end + +@implementation SNTMetric { + @private + /** Fully qualified metric name e.g. /ops/security/santa. */ + NSString *_name; + /** A help text for the metric to be exported to be exported. **/ + NSString *_help; + + /** Sorted list of the fieldNames **/ + NSArray *_fieldNames; + /** Mapping of field values to actual metric values (e.g. metric /proc/cpu_usage @"mode"=@"user" + * -> 0.89 */ + NSMutableDictionary *, SNTMetricValue *> *_metricsForFieldValues; + /** the type of metric this is e.g. counter, gauge etc. **/ + SNTMetricType _type; +} + +- (instancetype)initWithName:(NSString *)name + fieldNames:(NSArray *)fieldNames + helpText:(NSString *)help + type:(SNTMetricType)type { + self = [super init]; + if (self) { + _name = [name copy]; + _help = [help copy]; + _fieldNames = [[NSArray alloc] initWithArray:fieldNames copyItems:YES]; + _metricsForFieldValues = [[NSMutableDictionary alloc] init]; + _type = type; + } + return self; +} + +- (NSString *)name { + return _name; +} + +- (BOOL)hasSameSchemaAsMetric:(SNTMetric *)other { + if (![other isKindOfClass:[self class]]) { + return NO; + } + return [_name isEqualToString:other->_name] && [_help isEqualToString:other->_help] && + [_fieldNames isEqualTo:other->_fieldNames] && _type == other->_type; +} + +/** Retrieves the SNTMetricValue for a given field value. + Creates a new SNTMetricValue if none is present. */ +- (SNTMetricValue *)metricValueForFieldValues:(NSArray *)fieldValues { + NSParameterAssert(fieldValues.count == _fieldNames.count); + SNTMetricValue *metricValue = nil; + @synchronized(self) { + metricValue = _metricsForFieldValues[fieldValues]; + + if (!metricValue) { + // Deep copy to prevent mutations to the keys we store in the dictionary. + fieldValues = [[NSArray alloc] initWithArray:fieldValues copyItems:YES]; + metricValue = [[SNTMetricValue alloc] init]; + _metricsForFieldValues[fieldValues] = metricValue; + } + } + + return metricValue; +} + +- (NSDictionary *)encodeMetricValueForFieldValues:(NSArray *)fieldValues { + SNTMetricValue *metricValue = _metricsForFieldValues[fieldValues]; + + NSMutableDictionary *fieldDict = [[NSMutableDictionary alloc] init]; + + fieldDict[@"created"] = [metricValue getCreatedTimestamp]; + fieldDict[@"last_updated"] = [metricValue getLastUpdatedTimestamp]; + fieldDict[@"value"] = [fieldValues componentsJoinedByString:@","]; + + switch (_type) { + case SNTMetricTypeConstantBool: + case SNTMetricTypeBoolGauge: + fieldDict[@"data"] = [NSNumber numberWithBool:[metricValue getBoolValue]]; + break; + case SNTMetricTypeConstantInt64: + case SNTMetricTypeCounter: + case SNTMetricTypeInt64Gauge: + fieldDict[@"data"] = [NSNumber numberWithLongLong:[metricValue getInt64Value]]; + break; + case SNTMetricTypeConstantDouble: + case SNTMetricTypeDoubleGauge: + fieldDict[@"data"] = [NSNumber numberWithDouble:[metricValue getDoubleValue]]; + break; + case SNTMetricTypeConstantString: + case SNTMetricTypeStringGauge: fieldDict[@"data"] = [metricValue getStringValue]; break; + default: break; + } + return [NSDictionary dictionaryWithDictionary:fieldDict]; +} + +- (NSDictionary *)exportNSDictionary { + NSMutableDictionary *metricDict = [[NSMutableDictionary alloc] init]; + metricDict[@"type"] = [NSNumber numberWithInt:(int)_type]; + metricDict[@"fields"] = [[NSMutableDictionary alloc] init]; + + if (_fieldNames.count == 0) { + metricDict[@"fields"][@""] = @[ [self encodeMetricValueForFieldValues:@[]] ]; + } else { + for (NSString *fieldName in _fieldNames) { + NSMutableArray *fieldVals = [[NSMutableArray alloc] init]; + + for (NSArray *fieldValues in _metricsForFieldValues) { + [fieldVals addObject:[self encodeMetricValueForFieldValues:fieldValues]]; + } + + metricDict[@"fields"][fieldName] = fieldVals; + } + } + return [NSDictionary dictionaryWithDictionary:metricDict]; +} +@end + +@implementation SNTMetricCounter + +- (instancetype)initWithName:(NSString *)name + fieldNames:(NSArray *)fieldNames + helpText:(NSString *)helpText { + return [super initWithName:name + fieldNames:fieldNames + helpText:helpText + type:SNTMetricTypeCounter]; +} + +- (void)incrementBy:(long long)step forFieldValues:(NSArray *)fieldValues { + SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues]; + + if (!metricValue) { + return; + } + [metricValue addInt64:step]; +} + +- (void)incrementForFieldValues:(NSArray *)fieldValues { + [self incrementBy:1 forFieldValues:fieldValues]; +} + +- (long long)getCountForFieldValues:(NSArray *)fieldValues { + SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues]; + + if (!metricValue) { + return -1; + } + + return [metricValue getInt64Value]; +} +@end + +@implementation SNTMetricInt64Gauge +- (instancetype)initWithName:(NSString *)name + fieldNames:(NSArray *)fieldNames + helpText:(NSString *)helpText { + return [super initWithName:name + fieldNames:fieldNames + helpText:helpText + type:SNTMetricTypeInt64Gauge]; +} + +- (void)set:(long long)value forFieldValues:(NSArray *)fieldValues { + SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues]; + [metricValue setInt64:value]; +} + +- (long long)getGaugeValueForFieldValues:(NSArray *)fieldValues { + SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues]; + + if (!metricValue) { + return -1; + } + + return [metricValue getInt64Value]; +} +@end + +@implementation SNTMetricDoubleGauge + +- (instancetype)initWithName:(NSString *)name + fieldNames:(NSArray *)fieldNames + helpText:(NSString *)text { + return [super initWithName:name + fieldNames:fieldNames + helpText:text + type:SNTMetricTypeDoubleGauge]; +} + +- (void)set:(double)value forFieldValues:(NSArray *)fieldValues { + SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues]; + [metricValue setDouble:value]; +} + +- (double)getGaugeValueForFieldValues:(NSArray *)fieldValues { + SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues]; + + if (!metricValue) { + return -1; + } + + return [metricValue getDoubleValue]; +} +@end + +@implementation SNTMetricStringGauge +- (instancetype)initWithName:(NSString *)name + fieldNames:(NSArray *)fieldNames + helpText:(NSString *)text { + return [super initWithName:name + fieldNames:fieldNames + helpText:text + type:SNTMetricTypeStringGauge]; +} + +- (void)set:(NSString *)value forFieldValues:(NSArray *)fieldValues { + SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues]; + [metricValue setString:value]; +} + +- (NSString *)getStringValueForFieldValues:(NSArray *)fieldValues { + SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues]; + + if (!metricValue) { + return nil; + } + + return [metricValue getStringValue]; +} +@end + +@implementation SNTMetricBooleanGauge +- (instancetype)initWithName:(NSString *)name + fieldNames:(NSArray *)fieldNames + helpText:(NSString *)helpText { + return [super initWithName:name + fieldNames:fieldNames + helpText:helpText + type:SNTMetricTypeBoolGauge]; +} + +- (void)set:(BOOL)value forFieldValues:(NSArray *)fieldValues { + SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues]; + [metricValue setBool:value]; +} + +- (BOOL)getBoolValueForFieldValues:(NSArray *)fieldValues { + SNTMetricValue *metricValue = [self metricValueForFieldValues:fieldValues]; + + if (!metricValue) { + return false; + } + + return [metricValue getBoolValue]; +} +@end + +/** + * SNTMetricSet is the top level container for all metrics and metrics value + * its is abstracted from specific implementations but is close to Google's + * Monarch and Prometheus formats. + */ +@implementation SNTMetricSet { + @private + /** Labels that are used to identify the entity to that all metrics apply to. */ + NSMutableDictionary *_rootLabels; + /** Registered metrics keyed by name */ + NSMutableDictionary *_metrics; + + /** Callbacks to update metric values before exporting metrics */ + NSMutableArray *_callbacks; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _rootLabels = [[NSMutableDictionary alloc] init]; + _metrics = [[NSMutableDictionary alloc] init]; + _callbacks = [[NSMutableArray alloc] init]; + } + return self; +} + +- (instancetype)initWithHostname:(NSString *)hostname username:(NSString *)username { + self = [super init]; + if (self) { + _rootLabels = [[NSMutableDictionary alloc] init]; + _metrics = [[NSMutableDictionary alloc] init]; + _callbacks = [[NSMutableArray alloc] init]; + + _rootLabels[@"hostname"] = [hostname copy]; + _rootLabels[@"username"] = [username copy]; + } + + return self; +} + +- (void)addRootLabel:(NSString *)label value:(NSString *)value { + @synchronized(self) { + _rootLabels[label] = value; + } +} + +- (SNTMetric *)registerMetric:(nonnull SNTMetric *)metric { + @synchronized(self) { + SNTMetric *oldMetric = _metrics[[metric name]]; + if ([oldMetric hasSameSchemaAsMetric:metric]) { + return oldMetric; + } + NSAssert(!oldMetric, @"metric registered twice: %@", metric.name); + _metrics[metric.name] = metric; + } + return metric; +} + +- (void)registerCallback:(void (^)(void))callback { + @synchronized(self) { + [_callbacks addObject:callback]; + } +} + +- (SNTMetricCounter *)counterWithName:(NSString *)name + fieldNames:(NSArray *)fieldNames + helpText:(NSString *)helpText { + SNTMetricCounter *c = [[SNTMetricCounter alloc] initWithName:name + fieldNames:fieldNames + helpText:helpText]; + [self registerMetric:c]; + return c; +} + +- (SNTMetricInt64Gauge *)int64GaugeWithName:(NSString *)name + fieldNames:(NSArray *)fieldNames + helpText:(NSString *)helpText { + SNTMetricInt64Gauge *g = [[SNTMetricInt64Gauge alloc] initWithName:name + fieldNames:fieldNames + helpText:helpText]; + [self registerMetric:g]; + return g; +} + +- (SNTMetricDoubleGauge *)doubleGaugeWithName:(NSString *)name + fieldNames:(NSArray *)fieldNames + helpText:(NSString *)helpText { + SNTMetricDoubleGauge *g = [[SNTMetricDoubleGauge alloc] initWithName:name + fieldNames:fieldNames + helpText:helpText]; + + [self registerMetric:g]; + return g; +} + +- (SNTMetricStringGauge *)stringGaugeWithName:(NSString *)name + fieldNames:(NSArray *)fieldNames + helpText:(NSString *)helpText { + SNTMetricStringGauge *s = [[SNTMetricStringGauge alloc] initWithName:name + fieldNames:fieldNames + helpText:helpText]; + + [self registerMetric:s]; + return s; +} + +- (SNTMetricBooleanGauge *)booleanGaugeWithName:(NSString *)name + fieldNames:(NSArray *)fieldNames + helpText:(NSString *)helpText { + SNTMetricBooleanGauge *b = [[SNTMetricBooleanGauge alloc] initWithName:name + fieldNames:fieldNames + helpText:helpText]; + + [self registerMetric:b]; + return b; +} + +- (void)addConstantStringWithName:(NSString *)name + helpText:(NSString *)helpText + value:(NSString *)value { + SNTMetric *metric = [[SNTMetric alloc] initWithName:name + fieldNames:@[] + helpText:helpText + type:SNTMetricTypeConstantString]; + + SNTMetricValue *metricValue = [metric metricValueForFieldValues:@[]]; + [metricValue setString:value]; + [self registerMetric:metric]; +} + +- (void)addConstantIntegerWithName:(NSString *)name + helpText:(NSString *)helpText + value:(long long)value { + SNTMetric *metric = [[SNTMetric alloc] initWithName:name + fieldNames:@[] + helpText:helpText + type:SNTMetricTypeConstantInt64]; + + SNTMetricValue *metricValue = [metric metricValueForFieldValues:@[]]; + [metricValue setInt64:value]; + [self registerMetric:metric]; +} + +- (void)addConstantBooleanWithName:(NSString *)name + helpText:(NSString *)helpText + value:(BOOL)value { + SNTMetric *metric = [[SNTMetric alloc] initWithName:name + fieldNames:@[] + helpText:helpText + type:SNTMetricTypeConstantBool]; + + SNTMetricValue *metricValue = [metric metricValueForFieldValues:@[]]; + [metricValue setBool:value]; + [self registerMetric:metric]; +} + +/** exportDictionary exports the SNTMetricSet as a dictinary for use */ +- (NSDictionary *)exportNSDictionary { + NSDictionary *exported = nil; + + // Invoke callbacks to ensure metrics are up to date. + for (void (^cb)(void) in _callbacks) { + cb(); + } + + @synchronized(self) { + // Walk root labels + NSMutableDictionary *exportDict = [[NSMutableDictionary alloc] init]; + exportDict[@"root_labels"] = [NSDictionary dictionaryWithDictionary:_rootLabels]; + exportDict[@"metrics"] = [[NSMutableDictionary alloc] init]; + + // sort the metrics so we always get the same output. + for (id metricName in _metrics) { + SNTMetric *metric = [_metrics objectForKey:metricName]; + exportDict[@"metrics"][metricName] = [metric exportNSDictionary]; + } + + exported = [NSDictionary dictionaryWithDictionary:exportDict]; + } + return exported; +} +@end diff --git a/Source/common/SNTMetricSetTest.m b/Source/common/SNTMetricSetTest.m new file mode 100644 index 000000000..3f55cdf64 --- /dev/null +++ b/Source/common/SNTMetricSetTest.m @@ -0,0 +1,527 @@ +#import + +#import "Source/common/SNTMetricSet.h" + +@interface SNTMetricCounterTest : XCTestCase +@end + +@interface SNTMetricInt64GaugeTest : XCTestCase +@end + +@interface SNTMetricDoubleGaugeTest : XCTestCase +@end + +@interface SNTMetricBooleanGaugeTest : XCTestCase +@end + +@interface SNTMetricStringGaugeTest : XCTestCase +@end + +@interface SNTMetricSetTest : XCTestCase +@end + +// Stub out NSDate's date method +@implementation NSDate (custom) + ++ (instancetype)date { + NSDateFormatter *formatter = NSDateFormatter.new; + [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ssZZZ"]; + return [formatter dateFromString:@"2021-08-05 13:00:10+0000"]; +} + +@end + +@implementation SNTMetricCounterTest +- (void)testSimpleCounter { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; + SNTMetricCounter *c = + [metricSet counterWithName:@"/santa/events" + fieldNames:@[ @"rule_type" ] + helpText:@"Count of exec events broken out by rule type."]; + + XCTAssertNotNil(c, @"Expected returned SNTMetricCounter to not be nil"); + [c incrementForFieldValues:@[ @"certificate" ]]; + XCTAssertEqual(1, [c getCountForFieldValues:@[ @"certificate" ]], + @"Counter not incremendted by 1"); + [c incrementBy:3 forFieldValues:@[ @"certificate" ]]; + XCTAssertEqual(4, [c getCountForFieldValues:@[ @"certificate" ]], + @"Counter not incremendted by 3"); +} + +- (void)testExportNSDictionary { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; + SNTMetricCounter *c = + [metricSet counterWithName:@"/santa/events" + fieldNames:@[ @"rule_type" ] + helpText:@"Count of exec events broken out by rule type."]; + + XCTAssertNotNil(c); + [c incrementForFieldValues:@[ @"certificate" ]]; + + NSDictionary *expected = @{ + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeCounter], + @"fields" : @{ + @"rule_type" : @[ @{ + @"value" : @"certificate", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : [NSNumber numberWithInt:1] + } ] + } + }; + + XCTAssertEqualObjects([c exportNSDictionary], expected); +} +@end + +@implementation SNTMetricBooleanGaugeTest +- (void)testSimpleGauge { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; + SNTMetricBooleanGauge *b = [metricSet booleanGaugeWithName:@"/santa/daemon_connected" + fieldNames:@[] + helpText:@"Is the daemon connected."]; + XCTAssertNotNil(b); + [b set:true forFieldValues:@[]]; + XCTAssertTrue([b getBoolValueForFieldValues:@[]]); + [b set:false forFieldValues:@[]]; + XCTAssertFalse([b getBoolValueForFieldValues:@[]]); +} + +- (void)testExportNSDictionary { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; + SNTMetricBooleanGauge *b = [metricSet booleanGaugeWithName:@"/santa/daemon_connected" + fieldNames:@[] + helpText:@"Is the daemon connected."]; + XCTAssertNotNil(b); + [b set:true forFieldValues:@[]]; + NSDictionary *expected = @{ + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeBoolGauge], + @"fields" : @{ + @"" : @[ @{ + @"value" : @"", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : [NSNumber numberWithBool:true] + } ] + } + }; + + NSDictionary *output = [b exportNSDictionary]; + XCTAssertEqualObjects(output, expected); +} +@end + +@implementation SNTMetricInt64GaugeTest +- (void)testSimpleGauge { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; + SNTMetricInt64Gauge *g = + [metricSet int64GaugeWithName:@"/santa/rules" + fieldNames:@[ @"rule_type" ] + helpText:@"Count of rules broken out by rule type."]; + + XCTAssertNotNil(g, @"Expected returned SNTMetricInt64Gauge to not be nil"); + // set from zero + [g set:250 forFieldValues:@[ @"binary" ]]; + XCTAssertEqual(250, [g getGaugeValueForFieldValues:@[ @"binary" ]]); + + // Increase the gauge + [g set:500 forFieldValues:@[ @"binary" ]]; + XCTAssertEqual(500, [g getGaugeValueForFieldValues:@[ @"binary" ]]); + // Decrease after increase + [g set:100 forFieldValues:@[ @"binary" ]]; + XCTAssertEqual(100, [g getGaugeValueForFieldValues:@[ @"binary" ]]); + // Increase after decrease + [g set:750 forFieldValues:@[ @"binary" ]]; + XCTAssertEqual(750, [g getGaugeValueForFieldValues:@[ @"binary" ]]); + // TODO: export the tree to JSON and confirm the structure is correct. +} + +- (void)testExportNSDictionary { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; + SNTMetricInt64Gauge *g = + [metricSet int64GaugeWithName:@"/santa/rules" + fieldNames:@[ @"rule_type" ] + helpText:@"Count of rules broken out by rule type."]; + + XCTAssertNotNil(g, @"Expected returned SNTMetricInt64Gauge to not be nil"); + // set from zero + [g set:250 forFieldValues:@[ @"binary" ]]; + XCTAssertEqual(250, [g getGaugeValueForFieldValues:@[ @"binary" ]]); + + NSDictionary *expected = @{ + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeInt64Gauge], + @"fields" : @{ + @"rule_type" : @[ @{ + @"value" : @"binary", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : [NSNumber numberWithInt:250] + } ] + } + }; + + XCTAssertEqualObjects([g exportNSDictionary], expected); +} +@end + +@implementation SNTMetricDoubleGaugeTest + +- (void)testSimpleGauge { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; + SNTMetricDoubleGauge *g = [metricSet doubleGaugeWithName:@"/proc/cpu_usage" + fieldNames:@[ @"mode" ] + helpText:@"CPU time consumed by this process."]; + + XCTAssertNotNil(g, @"Expected returned SNTMetricDoubleGauge to not be nil"); + // set from zero + [g set:(double)0.45 forFieldValues:@[ @"user" ]]; + XCTAssertEqual(0.45, [g getGaugeValueForFieldValues:@[ @"user" ]]); + + // Increase the gauge + [g set:(double)0.90 forFieldValues:@[ @"user" ]]; + XCTAssertEqual(0.90, [g getGaugeValueForFieldValues:@[ @"user" ]]); + // Decrease after increase + [g set:0.71 forFieldValues:@[ @"user" ]]; + XCTAssertEqual(0.71, [g getGaugeValueForFieldValues:@[ @"user" ]]); + // Increase after decrease + [g set:0.75 forFieldValues:@[ @"user" ]]; + XCTAssertEqual(0.75, [g getGaugeValueForFieldValues:@[ @"user" ]]); +} + +- (void)testExportNSDictionary { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; + SNTMetricDoubleGauge *g = [metricSet doubleGaugeWithName:@"/proc/cpu_usage" + fieldNames:@[ @"mode" ] + helpText:@"CPU time consumed by this process."]; + + XCTAssertNotNil(g, @"Expected returned SNTMetricDoubleGauge to not be nil"); + // set from zero + [g set:(double)0.45 forFieldValues:@[ @"user" ]]; + [g set:(double)0.90 forFieldValues:@[ @"system" ]]; + + NSDictionary *expected = @{ + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeDoubleGauge], + @"fields" : @{ + @"mode" : @[ + @{ + @"value" : @"user", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : [NSNumber numberWithDouble:0.45] + }, + @{ + @"value" : @"system", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : [NSNumber numberWithDouble:0.90] + } + ] + } + }; + XCTAssertEqualObjects([g exportNSDictionary], expected); +} +@end + +@implementation SNTMetricStringGaugeTest +- (void)testSimpleGauge { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; + SNTMetricStringGauge *s = [metricSet stringGaugeWithName:@"/santa/mode" + fieldNames:@[] + helpText:@"String description of the mode."]; + + XCTAssertNotNil(s); + [s set:@"testValue" forFieldValues:@[]]; + XCTAssertEqualObjects([s getStringValueForFieldValues:@[]], @"testValue"); +} +- (void)testExportNSDictionary { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; + SNTMetricStringGauge *s = [metricSet stringGaugeWithName:@"/santa/mode" + fieldNames:@[] + helpText:@"String description of the mode."]; + + XCTAssertNotNil(s); + [s set:@"testValue" forFieldValues:@[]]; + + NSDictionary *expected = @{ + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeStringGauge], + @"fields" : @{ + @"" : @[ @{ + @"value" : @"", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : @"testValue" + } ] + } + }; + + XCTAssertEqualObjects([s exportNSDictionary], expected); +} +@end + +@implementation SNTMetricSetTest +- (void)testRootLabels { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; + [metricSet addRootLabel:@"hostname" value:@"localhost"]; + + NSDictionary *expected = @{@"root_labels" : @{@"hostname" : @"localhost"}, @"metrics" : @{}}; + + NSDictionary *output = [metricSet exportNSDictionary]; + XCTAssertEqualObjects(output, expected); +} + +- (void)testDoubleRegisteringIncompatibleMetricsFails { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; + SNTMetricCounter *c = [metricSet counterWithName:@"/foo/bar" + fieldNames:@[ @"field" ] + helpText:@"lorem ipsum"]; + + XCTAssertNotNil(c); + XCTAssertThrows([metricSet counterWithName:@"/foo/bar" + fieldNames:@[ @"incompatible" ] + helpText:@"A little help text"], + @"Should raise error for incompatible field names"); + + XCTAssertThrows([metricSet counterWithName:@"/foo/bar" + fieldNames:@[ @"result" ] + helpText:@"INCOMPATIBLE"], + @"Should raise error for incompatible help text"); +} + +- (void)testRegisterCallback { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; + // Register a callback metric which increments by one before export + SNTMetricInt64Gauge *gauge = [metricSet int64GaugeWithName:@"/foo/bar" + fieldNames:@[] + helpText:@"Number of callbacks done"]; + __block int count = 0; + [metricSet registerCallback:^(void) { + count++; + [gauge set:count forFieldValues:@[]]; + }]; + + // ensure the callback is called. + [metricSet exportNSDictionary]; + + XCTAssertEqual([gauge getGaugeValueForFieldValues:@[]], 1); +} + +- (void)testAddConstantBool { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; + [metricSet addConstantBooleanWithName:@"/tautology" + helpText:@"The first rule of tautology club is the first rule" + value:YES]; + + NSDictionary *expected = @{ + @"/tautology" : @{ + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeConstantBool], + @"fields" : @{ + @"" : @[ @{ + @"value" : @"", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : [NSNumber numberWithBool:true] + } ] + } + } + }; + + XCTAssertEqualObjects([metricSet exportNSDictionary][@"metrics"], expected); +} + +- (void)testAddConstantString { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; + + [metricSet addConstantStringWithName:@"/build/label" + helpText:@"Build label for the binary" + value:@"20210806.0.1"]; + + NSDictionary *expected = @{ + @"/build/label" : @{ + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeConstantString], + @"fields" : @{ + @"" : @[ @{ + @"value" : @"", + + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : @"20210806.0.1" + } ] + } + } + }; + + XCTAssertEqualObjects([metricSet exportNSDictionary][@"metrics"], expected); +} + +- (void)testAddConstantInt { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; + [metricSet addConstantIntegerWithName:@"/deep/thought/answer" + helpText:@"Life, the universe, and everything" + value:42]; + + NSDictionary *expected = @{ + @"/deep/thought/answer" : @{ + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeConstantInt64], + @"fields" : @{ + @"" : @[ @{ + @"value" : @"", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : [NSNumber numberWithLongLong:42] + } ] + } + } + }; + + XCTAssertEqualObjects([metricSet exportNSDictionary][@"metrics"], expected); +} + +- (void)testExportNSDictionary { + SNTMetricSet *metricSet = [[SNTMetricSet alloc] initWithHostname:@"testHost" + username:@"testUser"]; + + // Add constants + [metricSet addConstantStringWithName:@"/build/label" + helpText:@"Software version running." + value:@"20210809.0.1"]; + [metricSet addConstantBooleanWithName:@"/santa/using_endpoint_security_framework" + helpText:@"Is santad using the endpoint security framework." + value:TRUE]; + [metricSet addConstantIntegerWithName:@"/proc/birth_timestamp" + helpText:@"Start time of this LogDumper instance, in microseconds " + @"since epoch" + value:(long long)(0x12345668910)]; + // Add Metrics + SNTMetricCounter *c = [metricSet counterWithName:@"/santa/events" + fieldNames:@[ @"rule_type" ] + helpText:@"Count of events on the host"]; + + [c incrementForFieldValues:@[ @"binary" ]]; + [c incrementBy:2 forFieldValues:@[ @"certificate" ]]; + + SNTMetricInt64Gauge *g = [metricSet int64GaugeWithName:@"/santa/rules" + fieldNames:@[ @"rule_type" ] + helpText:@"Number of rules."]; + + [g set:1 forFieldValues:@[ @"binary" ]]; + [g set:3 forFieldValues:@[ @"certificate" ]]; + + // Add Metrics with callback + SNTMetricInt64Gauge *virtualMemoryGauge = + [metricSet int64GaugeWithName:@"/proc/memory/virtual_size" + fieldNames:@[] + helpText:@"The virtual memory size of this process."]; + + SNTMetricInt64Gauge *residentMemoryGauge = + [metricSet int64GaugeWithName:@"/proc/memory/resident_size" + fieldNames:@[] + helpText:@"The resident set siz of this process."]; + + [metricSet registerCallback:^(void) { + [virtualMemoryGauge set:987654321 forFieldValues:@[]]; + [residentMemoryGauge set:123456789 forFieldValues:@[]]; + }]; + + NSDictionary *expected = @{ + @"root_labels" : @{@"hostname" : @"testHost", @"username" : @"testUser"}, + @"metrics" : @{ + @"/build/label" : @{ + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeConstantString], + @"fields" : @{ + @"" : @[ @{ + @"value" : @"", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : @"20210809.0.1" + } ] + } + }, + @"/santa/events" : @{ + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeCounter], + @"fields" : @{ + @"rule_type" : @[ + @{ + @"value" : @"binary", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : [NSNumber numberWithInt:1], + }, + @{ + @"value" : @"certificate", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : [NSNumber numberWithInt:2], + }, + ], + }, + }, + @"/santa/rules" : @{ + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeInt64Gauge], + @"fields" : @{ + @"rule_type" : @[ + @{ + @"value" : @"binary", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : [NSNumber numberWithInt:1], + }, + @{ + @"value" : @"certificate", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : [NSNumber numberWithInt:3], + } + ] + }, + }, + @"/santa/using_endpoint_security_framework" : @{ + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeConstantBool], + @"fields" : @{ + @"" : @[ @{ + @"value" : @"", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : [NSNumber numberWithBool:YES] + } ] + } + }, + @"/proc/birth_timestamp" : @{ + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeConstantInt64], + @"fields" : @{ + @"" : @[ @{ + @"value" : @"", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : [NSNumber numberWithLong:1250999830800] + } ] + }, + }, + @"/proc/memory/virtual_size" : @{ + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeInt64Gauge], + @"fields" : @{ + @"" : @[ @{ + @"value" : @"", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : [NSNumber numberWithInt:987654321] + } ] + } + }, + @"/proc/memory/resident_size" : @{ + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeInt64Gauge], + @"fields" : @{ + @"" : @[ @{ + @"value" : @"", + @"created" : [NSDate date], + @"last_updated" : [NSDate date], + @"data" : [NSNumber numberWithInt:123456789] + } ] + }, + }, + } + }; + + XCTAssertEqualObjects([metricSet exportNSDictionary], expected); +} + +@end From 40a7de879287e01e18872d5389dbaa5ebc36b178 Mon Sep 17 00:00:00 2001 From: Pete Markowsky Date: Wed, 11 Aug 2021 13:40:21 -0400 Subject: [PATCH 2/6] Removed accidentally included xcode Project file. --- Santa.xcodeproj/project.pbxproj | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Santa.xcodeproj/project.pbxproj b/Santa.xcodeproj/project.pbxproj index 14813cb05..c1881ea2f 100644 --- a/Santa.xcodeproj/project.pbxproj +++ b/Santa.xcodeproj/project.pbxproj @@ -8,8 +8,6 @@ /* Begin PBXBuildFile section */ 0D9F577C2342650F005D9AA8 /* SNTPrefixTree.cc in Sources */ = {isa = PBXBuildFile; fileRef = C7658B022322B84F00F36578 /* SNTPrefixTree.cc */; }; - 4D3AD70026B49EB400110D20 /* SNTMetricSetTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D3AD6FE26B49EB400110D20 /* SNTMetricSetTest.m */; }; - 4D3AD70126B49EB400110D20 /* SNTMetricSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D3AD6FF26B49EB400110D20 /* SNTMetricSet.m */; }; 59502195B2982225D3706DCE /* libPods-santabundleservice.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A55D73A235850B9FA991865 /* libPods-santabundleservice.a */; }; AD3736AF78C41A962C26D429 /* libPods-Santa.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C3E743944A9D77423AA1534 /* libPods-Santa.a */; }; B617F8907035AD3DA2CB5C45 /* libPods-syncservice.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D10E53C5ACA15304D9A7CC95 /* libPods-syncservice.a */; }; @@ -213,9 +211,6 @@ 2A55D73A235850B9FA991865 /* libPods-santabundleservice.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-santabundleservice.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 4A98E2E9727FBEF27CDDA298 /* Pods-syncservice.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-syncservice.debug.xcconfig"; path = "Target Support Files/Pods-syncservice/Pods-syncservice.debug.xcconfig"; sourceTree = ""; }; 4C3E743944A9D77423AA1534 /* libPods-Santa.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Santa.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 4D3AD6FD26B49EB400110D20 /* SNTMetricSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTMetricSet.h; sourceTree = ""; }; - 4D3AD6FE26B49EB400110D20 /* SNTMetricSetTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTMetricSetTest.m; sourceTree = ""; }; - 4D3AD6FF26B49EB400110D20 /* SNTMetricSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTMetricSet.m; sourceTree = ""; }; 4E28DBA012524ABF55F8300C /* Pods-com.google.santa.daemon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-com.google.santa.daemon.debug.xcconfig"; path = "Target Support Files/Pods-com.google.santa.daemon/Pods-com.google.santa.daemon.debug.xcconfig"; sourceTree = ""; }; 7AF15DF785BAA0EAB0BE340D /* Pods-santad.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santad.release.xcconfig"; path = "Target Support Files/Pods-santad/Pods-santad.release.xcconfig"; sourceTree = ""; }; 7FEBB0D42E35B93E5B995B06 /* Pods-syncservice.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-syncservice.release.xcconfig"; path = "Target Support Files/Pods-syncservice/Pods-syncservice.release.xcconfig"; sourceTree = ""; }; @@ -641,9 +636,6 @@ C7658AB02322B84F00F36578 /* common */ = { isa = PBXGroup; children = ( - 4D3AD6FD26B49EB400110D20 /* SNTMetricSet.h */, - 4D3AD6FF26B49EB400110D20 /* SNTMetricSet.m */, - 4D3AD6FE26B49EB400110D20 /* SNTMetricSetTest.m */, C7658AD52322B84F00F36578 /* BUILD */, C7658AD22322B84F00F36578 /* SNTBlockMessage.h */, C7658AC72322B84F00F36578 /* SNTBlockMessage.m */, @@ -1278,7 +1270,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 4D3AD70126B49EB400110D20 /* SNTMetricSet.m in Sources */, C7658B192322BDEA00F36578 /* SNTAppDelegate.m in Sources */, C7658B432322C12100F36578 /* SNTRule.m in Sources */, C7658B3D2322C0F500F36578 /* SNTXPCBundleServiceInterface.m in Sources */, @@ -1291,7 +1282,6 @@ C7658B172322BDE500F36578 /* SNTAboutWindowController.m in Sources */, C7658B402322C10700F36578 /* SNTLogging.m in Sources */, C7658B3E2322C0FC00F36578 /* SNTXPCControlInterface.m in Sources */, - 4D3AD70026B49EB400110D20 /* SNTMetricSetTest.m in Sources */, C7658B3C2322C0ED00F36578 /* SNTConfigurator.m in Sources */, C7658B3F2322C10200F36578 /* SNTXPCNotifierInterface.m in Sources */, C7658B442322C12600F36578 /* SNTSystemInfo.m in Sources */, From 48889297a793de44d01e32f40d34cde201f244ea Mon Sep 17 00:00:00 2001 From: Pete Markowsky Date: Wed, 11 Aug 2021 13:42:34 -0400 Subject: [PATCH 3/6] Added missing copyright notice. --- Source/common/SNTMetricSet.m | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Source/common/SNTMetricSet.m b/Source/common/SNTMetricSet.m index cae825ceb..6abf74709 100644 --- a/Source/common/SNTMetricSet.m +++ b/Source/common/SNTMetricSet.m @@ -1,3 +1,17 @@ +/// Copyright 2021 Google 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. + #import "SNTMetricSet.h" /** From 5a54a07d99f57c69e4fb6b83f8a4113766483dba Mon Sep 17 00:00:00 2001 From: Pete Markowsky Date: Wed, 11 Aug 2021 15:20:10 -0400 Subject: [PATCH 4/6] Addressed PR feedback. --- Source/common/SNTMetricSet.h | 18 ++++++------ Source/common/SNTMetricSet.m | 30 ++++++++++---------- Source/common/SNTMetricSetTest.m | 47 ++++++++++++++++---------------- 3 files changed, 46 insertions(+), 49 deletions(-) diff --git a/Source/common/SNTMetricSet.h b/Source/common/SNTMetricSet.h index 27e0e9a38..158f974ee 100644 --- a/Source/common/SNTMetricSet.h +++ b/Source/common/SNTMetricSet.h @@ -16,7 +16,7 @@ /** * Provides an abstraction for various metric systems that will be exported to - * monitoring systems via the MetricService. This is used store internal + * monitoring systems via the MetricService. This is used to store internal * counters and metrics that can be exported to an external monitoring system. * * `SNTMetricSet` for storing and creating metrics and counters. This is @@ -30,7 +30,7 @@ * * `SNTMetricGaugeInt64` * * `SNTMetricGaugeDouble` * * `SNTMetricString` - * * `SNTMetricBoolean` + * * `SNTMetricBool` */ NS_ASSUME_NONNULL_BEGIN @@ -41,17 +41,15 @@ typedef NS_ENUM(NSInteger, SNTMetricType) { SNTMetricTypeConstantString = 2, SNTMetricTypeConstantInt64 = 3, SNTMetricTypeConstantDouble = 4, - SNTMetricTypeBoolGauge = 5, - SNTMetricTypeStringGauge = 6, - SNTMetricTypeInt64Gauge = 7, - SNTMetricTypeDoubleGauge = 8, + SNTMetricTypeGaugeBool = 5, + SNTMetricTypeGaugeString = 6, + SNTMetricTypeGaugeInt64 = 7, + SNTMetricTypeGaugeDouble = 8, SNTMetricTypeCounter = 9, }; -/* - */ @interface SNTMetric : NSObject -- (NSDictionary *)exportNSDictionary; +- (NSDictionary *)export; @end @interface SNTMetricCounter : SNTMetric @@ -169,7 +167,7 @@ typedef NS_ENUM(NSInteger, SNTMetricType) { - (void)registerCallback:(void (^)(void))callback; /** Export creates an NSDictionary of the state of the metrics */ -- (NSDictionary *)exportNSDictionary; +- (NSDictionary *)export; @end NS_ASSUME_NONNULL_END diff --git a/Source/common/SNTMetricSet.m b/Source/common/SNTMetricSet.m index 6abf74709..1b85f0d59 100644 --- a/Source/common/SNTMetricSet.m +++ b/Source/common/SNTMetricSet.m @@ -238,27 +238,27 @@ - (NSDictionary *)encodeMetricValueForFieldValues:(NSArray *)fieldVa switch (_type) { case SNTMetricTypeConstantBool: - case SNTMetricTypeBoolGauge: + case SNTMetricTypeGaugeBool: fieldDict[@"data"] = [NSNumber numberWithBool:[metricValue getBoolValue]]; break; case SNTMetricTypeConstantInt64: case SNTMetricTypeCounter: - case SNTMetricTypeInt64Gauge: + case SNTMetricTypeGaugeInt64: fieldDict[@"data"] = [NSNumber numberWithLongLong:[metricValue getInt64Value]]; break; case SNTMetricTypeConstantDouble: - case SNTMetricTypeDoubleGauge: + case SNTMetricTypeGaugeDouble: fieldDict[@"data"] = [NSNumber numberWithDouble:[metricValue getDoubleValue]]; break; case SNTMetricTypeConstantString: - case SNTMetricTypeStringGauge: fieldDict[@"data"] = [metricValue getStringValue]; break; + case SNTMetricTypeGaugeString: fieldDict[@"data"] = [metricValue getStringValue]; break; default: break; } - return [NSDictionary dictionaryWithDictionary:fieldDict]; + return fieldDict; } -- (NSDictionary *)exportNSDictionary { - NSMutableDictionary *metricDict = [[NSMutableDictionary alloc] init]; +- (NSDictionary *)export { + NSMutableDictionary *metricDict = [NSMutableDictionary dictionaryWithCapacity:_fieldNames.count]; metricDict[@"type"] = [NSNumber numberWithInt:(int)_type]; metricDict[@"fields"] = [[NSMutableDictionary alloc] init]; @@ -275,7 +275,7 @@ - (NSDictionary *)exportNSDictionary { metricDict[@"fields"][fieldName] = fieldVals; } } - return [NSDictionary dictionaryWithDictionary:metricDict]; + return metricDict; } @end @@ -321,7 +321,7 @@ - (instancetype)initWithName:(NSString *)name return [super initWithName:name fieldNames:fieldNames helpText:helpText - type:SNTMetricTypeInt64Gauge]; + type:SNTMetricTypeGaugeInt64]; } - (void)set:(long long)value forFieldValues:(NSArray *)fieldValues { @@ -348,7 +348,7 @@ - (instancetype)initWithName:(NSString *)name return [super initWithName:name fieldNames:fieldNames helpText:text - type:SNTMetricTypeDoubleGauge]; + type:SNTMetricTypeGaugeDouble]; } - (void)set:(double)value forFieldValues:(NSArray *)fieldValues { @@ -374,7 +374,7 @@ - (instancetype)initWithName:(NSString *)name return [super initWithName:name fieldNames:fieldNames helpText:text - type:SNTMetricTypeStringGauge]; + type:SNTMetricTypeGaugeString]; } - (void)set:(NSString *)value forFieldValues:(NSArray *)fieldValues { @@ -400,7 +400,7 @@ - (instancetype)initWithName:(NSString *)name return [super initWithName:name fieldNames:fieldNames helpText:helpText - type:SNTMetricTypeBoolGauge]; + type:SNTMetricTypeGaugeBool]; } - (void)set:(BOOL)value forFieldValues:(NSArray *)fieldValues { @@ -575,8 +575,8 @@ - (void)addConstantBooleanWithName:(NSString *)name [self registerMetric:metric]; } -/** exportDictionary exports the SNTMetricSet as a dictinary for use */ -- (NSDictionary *)exportNSDictionary { +/** Export current state of the SNTMetricSet as an NSDictionary. */ +- (NSDictionary *)export { NSDictionary *exported = nil; // Invoke callbacks to ensure metrics are up to date. @@ -593,7 +593,7 @@ - (NSDictionary *)exportNSDictionary { // sort the metrics so we always get the same output. for (id metricName in _metrics) { SNTMetric *metric = [_metrics objectForKey:metricName]; - exportDict[@"metrics"][metricName] = [metric exportNSDictionary]; + exportDict[@"metrics"][metricName] = [metric export]; } exported = [NSDictionary dictionaryWithDictionary:exportDict]; diff --git a/Source/common/SNTMetricSetTest.m b/Source/common/SNTMetricSetTest.m index 3f55cdf64..4003b5390 100644 --- a/Source/common/SNTMetricSetTest.m +++ b/Source/common/SNTMetricSetTest.m @@ -5,7 +5,7 @@ @interface SNTMetricCounterTest : XCTestCase @end -@interface SNTMetricInt64GaugeTest : XCTestCase +@interface SNTMetricGaugeInt64Test : XCTestCase @end @interface SNTMetricDoubleGaugeTest : XCTestCase @@ -70,7 +70,7 @@ - (void)testExportNSDictionary { } }; - XCTAssertEqualObjects([c exportNSDictionary], expected); + XCTAssertEqualObjects([c export], expected); } @end @@ -95,7 +95,7 @@ - (void)testExportNSDictionary { XCTAssertNotNil(b); [b set:true forFieldValues:@[]]; NSDictionary *expected = @{ - @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeBoolGauge], + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeBool], @"fields" : @{ @"" : @[ @{ @"value" : @"", @@ -106,12 +106,12 @@ - (void)testExportNSDictionary { } }; - NSDictionary *output = [b exportNSDictionary]; + NSDictionary *output = [b export]; XCTAssertEqualObjects(output, expected); } @end -@implementation SNTMetricInt64GaugeTest +@implementation SNTMetricGaugeInt64Test - (void)testSimpleGauge { SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; SNTMetricInt64Gauge *g = @@ -119,7 +119,7 @@ - (void)testSimpleGauge { fieldNames:@[ @"rule_type" ] helpText:@"Count of rules broken out by rule type."]; - XCTAssertNotNil(g, @"Expected returned SNTMetricInt64Gauge to not be nil"); + XCTAssertNotNil(g, @"Expected returned SNTMetricGaugeInt64 to not be nil"); // set from zero [g set:250 forFieldValues:@[ @"binary" ]]; XCTAssertEqual(250, [g getGaugeValueForFieldValues:@[ @"binary" ]]); @@ -143,13 +143,13 @@ - (void)testExportNSDictionary { fieldNames:@[ @"rule_type" ] helpText:@"Count of rules broken out by rule type."]; - XCTAssertNotNil(g, @"Expected returned SNTMetricInt64Gauge to not be nil"); + XCTAssertNotNil(g, @"Expected returned SNTMetricGaugeInt64 to not be nil"); // set from zero [g set:250 forFieldValues:@[ @"binary" ]]; XCTAssertEqual(250, [g getGaugeValueForFieldValues:@[ @"binary" ]]); NSDictionary *expected = @{ - @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeInt64Gauge], + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeInt64], @"fields" : @{ @"rule_type" : @[ @{ @"value" : @"binary", @@ -160,7 +160,7 @@ - (void)testExportNSDictionary { } }; - XCTAssertEqualObjects([g exportNSDictionary], expected); + XCTAssertEqualObjects([g export], expected); } @end @@ -200,7 +200,7 @@ - (void)testExportNSDictionary { [g set:(double)0.90 forFieldValues:@[ @"system" ]]; NSDictionary *expected = @{ - @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeDoubleGauge], + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeDouble], @"fields" : @{ @"mode" : @[ @{ @@ -218,7 +218,7 @@ - (void)testExportNSDictionary { ] } }; - XCTAssertEqualObjects([g exportNSDictionary], expected); + XCTAssertEqualObjects([g export], expected); } @end @@ -243,7 +243,7 @@ - (void)testExportNSDictionary { [s set:@"testValue" forFieldValues:@[]]; NSDictionary *expected = @{ - @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeStringGauge], + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeString], @"fields" : @{ @"" : @[ @{ @"value" : @"", @@ -254,7 +254,7 @@ - (void)testExportNSDictionary { } }; - XCTAssertEqualObjects([s exportNSDictionary], expected); + XCTAssertEqualObjects([s export], expected); } @end @@ -265,7 +265,7 @@ - (void)testRootLabels { NSDictionary *expected = @{@"root_labels" : @{@"hostname" : @"localhost"}, @"metrics" : @{}}; - NSDictionary *output = [metricSet exportNSDictionary]; + NSDictionary *output = [metricSet export]; XCTAssertEqualObjects(output, expected); } @@ -300,7 +300,7 @@ - (void)testRegisterCallback { }]; // ensure the callback is called. - [metricSet exportNSDictionary]; + [metricSet export]; XCTAssertEqual([gauge getGaugeValueForFieldValues:@[]], 1); } @@ -325,7 +325,7 @@ - (void)testAddConstantBool { } }; - XCTAssertEqualObjects([metricSet exportNSDictionary][@"metrics"], expected); + XCTAssertEqualObjects([metricSet export][@"metrics"], expected); } - (void)testAddConstantString { @@ -341,7 +341,6 @@ - (void)testAddConstantString { @"fields" : @{ @"" : @[ @{ @"value" : @"", - @"created" : [NSDate date], @"last_updated" : [NSDate date], @"data" : @"20210806.0.1" @@ -350,7 +349,7 @@ - (void)testAddConstantString { } }; - XCTAssertEqualObjects([metricSet exportNSDictionary][@"metrics"], expected); + XCTAssertEqualObjects([metricSet export][@"metrics"], expected); } - (void)testAddConstantInt { @@ -373,7 +372,7 @@ - (void)testAddConstantInt { } }; - XCTAssertEqualObjects([metricSet exportNSDictionary][@"metrics"], expected); + XCTAssertEqualObjects([metricSet export][@"metrics"], expected); } - (void)testExportNSDictionary { @@ -416,7 +415,7 @@ - (void)testExportNSDictionary { [metricSet int64GaugeWithName:@"/proc/memory/resident_size" fieldNames:@[] helpText:@"The resident set siz of this process."]; - + [metricSet registerCallback:^(void) { [virtualMemoryGauge set:987654321 forFieldValues:@[]]; [residentMemoryGauge set:123456789 forFieldValues:@[]]; @@ -456,7 +455,7 @@ - (void)testExportNSDictionary { }, }, @"/santa/rules" : @{ - @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeInt64Gauge], + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeInt64], @"fields" : @{ @"rule_type" : @[ @{ @@ -497,7 +496,7 @@ - (void)testExportNSDictionary { }, }, @"/proc/memory/virtual_size" : @{ - @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeInt64Gauge], + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeInt64], @"fields" : @{ @"" : @[ @{ @"value" : @"", @@ -508,7 +507,7 @@ - (void)testExportNSDictionary { } }, @"/proc/memory/resident_size" : @{ - @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeInt64Gauge], + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeInt64], @"fields" : @{ @"" : @[ @{ @"value" : @"", @@ -521,7 +520,7 @@ - (void)testExportNSDictionary { } }; - XCTAssertEqualObjects([metricSet exportNSDictionary], expected); + XCTAssertEqualObjects([metricSet export], expected); } @end From 8bb733cf321d3b7e7c55092bab1a9fcf70c7e410 Mon Sep 17 00:00:00 2001 From: Pete Markowsky Date: Wed, 11 Aug 2021 15:20:10 -0400 Subject: [PATCH 5/6] Addressed PR feedback. --- Source/common/SNTMetricSet.h | 18 ++++++------ Source/common/SNTMetricSet.m | 36 +++++++++++------------- Source/common/SNTMetricSetTest.m | 47 ++++++++++++++++---------------- 3 files changed, 47 insertions(+), 54 deletions(-) diff --git a/Source/common/SNTMetricSet.h b/Source/common/SNTMetricSet.h index 27e0e9a38..158f974ee 100644 --- a/Source/common/SNTMetricSet.h +++ b/Source/common/SNTMetricSet.h @@ -16,7 +16,7 @@ /** * Provides an abstraction for various metric systems that will be exported to - * monitoring systems via the MetricService. This is used store internal + * monitoring systems via the MetricService. This is used to store internal * counters and metrics that can be exported to an external monitoring system. * * `SNTMetricSet` for storing and creating metrics and counters. This is @@ -30,7 +30,7 @@ * * `SNTMetricGaugeInt64` * * `SNTMetricGaugeDouble` * * `SNTMetricString` - * * `SNTMetricBoolean` + * * `SNTMetricBool` */ NS_ASSUME_NONNULL_BEGIN @@ -41,17 +41,15 @@ typedef NS_ENUM(NSInteger, SNTMetricType) { SNTMetricTypeConstantString = 2, SNTMetricTypeConstantInt64 = 3, SNTMetricTypeConstantDouble = 4, - SNTMetricTypeBoolGauge = 5, - SNTMetricTypeStringGauge = 6, - SNTMetricTypeInt64Gauge = 7, - SNTMetricTypeDoubleGauge = 8, + SNTMetricTypeGaugeBool = 5, + SNTMetricTypeGaugeString = 6, + SNTMetricTypeGaugeInt64 = 7, + SNTMetricTypeGaugeDouble = 8, SNTMetricTypeCounter = 9, }; -/* - */ @interface SNTMetric : NSObject -- (NSDictionary *)exportNSDictionary; +- (NSDictionary *)export; @end @interface SNTMetricCounter : SNTMetric @@ -169,7 +167,7 @@ typedef NS_ENUM(NSInteger, SNTMetricType) { - (void)registerCallback:(void (^)(void))callback; /** Export creates an NSDictionary of the state of the metrics */ -- (NSDictionary *)exportNSDictionary; +- (NSDictionary *)export; @end NS_ASSUME_NONNULL_END diff --git a/Source/common/SNTMetricSet.m b/Source/common/SNTMetricSet.m index 6abf74709..8c4d1016e 100644 --- a/Source/common/SNTMetricSet.m +++ b/Source/common/SNTMetricSet.m @@ -73,10 +73,6 @@ @implementation SNTMetricValue { - (instancetype)init { self = [super init]; if (self) { - _int64Value = 0; - _doubleValue = 0; - _stringValue = nil; - _boolValue = NO; _creationTime = [NSDate date]; _lastUpdate = _creationTime; } @@ -189,7 +185,7 @@ - (instancetype)initWithName:(NSString *)name if (self) { _name = [name copy]; _help = [help copy]; - _fieldNames = [[NSArray alloc] initWithArray:fieldNames copyItems:YES]; + _fieldNames = [fieldNames copy]; _metricsForFieldValues = [[NSMutableDictionary alloc] init]; _type = type; } @@ -238,27 +234,27 @@ - (NSDictionary *)encodeMetricValueForFieldValues:(NSArray *)fieldVa switch (_type) { case SNTMetricTypeConstantBool: - case SNTMetricTypeBoolGauge: + case SNTMetricTypeGaugeBool: fieldDict[@"data"] = [NSNumber numberWithBool:[metricValue getBoolValue]]; break; case SNTMetricTypeConstantInt64: case SNTMetricTypeCounter: - case SNTMetricTypeInt64Gauge: + case SNTMetricTypeGaugeInt64: fieldDict[@"data"] = [NSNumber numberWithLongLong:[metricValue getInt64Value]]; break; case SNTMetricTypeConstantDouble: - case SNTMetricTypeDoubleGauge: + case SNTMetricTypeGaugeDouble: fieldDict[@"data"] = [NSNumber numberWithDouble:[metricValue getDoubleValue]]; break; case SNTMetricTypeConstantString: - case SNTMetricTypeStringGauge: fieldDict[@"data"] = [metricValue getStringValue]; break; + case SNTMetricTypeGaugeString: fieldDict[@"data"] = [metricValue getStringValue]; break; default: break; } - return [NSDictionary dictionaryWithDictionary:fieldDict]; + return fieldDict; } -- (NSDictionary *)exportNSDictionary { - NSMutableDictionary *metricDict = [[NSMutableDictionary alloc] init]; +- (NSDictionary *)export { + NSMutableDictionary *metricDict = [NSMutableDictionary dictionaryWithCapacity:_fieldNames.count]; metricDict[@"type"] = [NSNumber numberWithInt:(int)_type]; metricDict[@"fields"] = [[NSMutableDictionary alloc] init]; @@ -275,7 +271,7 @@ - (NSDictionary *)exportNSDictionary { metricDict[@"fields"][fieldName] = fieldVals; } } - return [NSDictionary dictionaryWithDictionary:metricDict]; + return metricDict; } @end @@ -321,7 +317,7 @@ - (instancetype)initWithName:(NSString *)name return [super initWithName:name fieldNames:fieldNames helpText:helpText - type:SNTMetricTypeInt64Gauge]; + type:SNTMetricTypeGaugeInt64]; } - (void)set:(long long)value forFieldValues:(NSArray *)fieldValues { @@ -348,7 +344,7 @@ - (instancetype)initWithName:(NSString *)name return [super initWithName:name fieldNames:fieldNames helpText:text - type:SNTMetricTypeDoubleGauge]; + type:SNTMetricTypeGaugeDouble]; } - (void)set:(double)value forFieldValues:(NSArray *)fieldValues { @@ -374,7 +370,7 @@ - (instancetype)initWithName:(NSString *)name return [super initWithName:name fieldNames:fieldNames helpText:text - type:SNTMetricTypeStringGauge]; + type:SNTMetricTypeGaugeString]; } - (void)set:(NSString *)value forFieldValues:(NSArray *)fieldValues { @@ -400,7 +396,7 @@ - (instancetype)initWithName:(NSString *)name return [super initWithName:name fieldNames:fieldNames helpText:helpText - type:SNTMetricTypeBoolGauge]; + type:SNTMetricTypeGaugeBool]; } - (void)set:(BOOL)value forFieldValues:(NSArray *)fieldValues { @@ -575,8 +571,8 @@ - (void)addConstantBooleanWithName:(NSString *)name [self registerMetric:metric]; } -/** exportDictionary exports the SNTMetricSet as a dictinary for use */ -- (NSDictionary *)exportNSDictionary { +/** Export current state of the SNTMetricSet as an NSDictionary. */ +- (NSDictionary *)export { NSDictionary *exported = nil; // Invoke callbacks to ensure metrics are up to date. @@ -593,7 +589,7 @@ - (NSDictionary *)exportNSDictionary { // sort the metrics so we always get the same output. for (id metricName in _metrics) { SNTMetric *metric = [_metrics objectForKey:metricName]; - exportDict[@"metrics"][metricName] = [metric exportNSDictionary]; + exportDict[@"metrics"][metricName] = [metric export]; } exported = [NSDictionary dictionaryWithDictionary:exportDict]; diff --git a/Source/common/SNTMetricSetTest.m b/Source/common/SNTMetricSetTest.m index 3f55cdf64..4003b5390 100644 --- a/Source/common/SNTMetricSetTest.m +++ b/Source/common/SNTMetricSetTest.m @@ -5,7 +5,7 @@ @interface SNTMetricCounterTest : XCTestCase @end -@interface SNTMetricInt64GaugeTest : XCTestCase +@interface SNTMetricGaugeInt64Test : XCTestCase @end @interface SNTMetricDoubleGaugeTest : XCTestCase @@ -70,7 +70,7 @@ - (void)testExportNSDictionary { } }; - XCTAssertEqualObjects([c exportNSDictionary], expected); + XCTAssertEqualObjects([c export], expected); } @end @@ -95,7 +95,7 @@ - (void)testExportNSDictionary { XCTAssertNotNil(b); [b set:true forFieldValues:@[]]; NSDictionary *expected = @{ - @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeBoolGauge], + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeBool], @"fields" : @{ @"" : @[ @{ @"value" : @"", @@ -106,12 +106,12 @@ - (void)testExportNSDictionary { } }; - NSDictionary *output = [b exportNSDictionary]; + NSDictionary *output = [b export]; XCTAssertEqualObjects(output, expected); } @end -@implementation SNTMetricInt64GaugeTest +@implementation SNTMetricGaugeInt64Test - (void)testSimpleGauge { SNTMetricSet *metricSet = [[SNTMetricSet alloc] init]; SNTMetricInt64Gauge *g = @@ -119,7 +119,7 @@ - (void)testSimpleGauge { fieldNames:@[ @"rule_type" ] helpText:@"Count of rules broken out by rule type."]; - XCTAssertNotNil(g, @"Expected returned SNTMetricInt64Gauge to not be nil"); + XCTAssertNotNil(g, @"Expected returned SNTMetricGaugeInt64 to not be nil"); // set from zero [g set:250 forFieldValues:@[ @"binary" ]]; XCTAssertEqual(250, [g getGaugeValueForFieldValues:@[ @"binary" ]]); @@ -143,13 +143,13 @@ - (void)testExportNSDictionary { fieldNames:@[ @"rule_type" ] helpText:@"Count of rules broken out by rule type."]; - XCTAssertNotNil(g, @"Expected returned SNTMetricInt64Gauge to not be nil"); + XCTAssertNotNil(g, @"Expected returned SNTMetricGaugeInt64 to not be nil"); // set from zero [g set:250 forFieldValues:@[ @"binary" ]]; XCTAssertEqual(250, [g getGaugeValueForFieldValues:@[ @"binary" ]]); NSDictionary *expected = @{ - @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeInt64Gauge], + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeInt64], @"fields" : @{ @"rule_type" : @[ @{ @"value" : @"binary", @@ -160,7 +160,7 @@ - (void)testExportNSDictionary { } }; - XCTAssertEqualObjects([g exportNSDictionary], expected); + XCTAssertEqualObjects([g export], expected); } @end @@ -200,7 +200,7 @@ - (void)testExportNSDictionary { [g set:(double)0.90 forFieldValues:@[ @"system" ]]; NSDictionary *expected = @{ - @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeDoubleGauge], + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeDouble], @"fields" : @{ @"mode" : @[ @{ @@ -218,7 +218,7 @@ - (void)testExportNSDictionary { ] } }; - XCTAssertEqualObjects([g exportNSDictionary], expected); + XCTAssertEqualObjects([g export], expected); } @end @@ -243,7 +243,7 @@ - (void)testExportNSDictionary { [s set:@"testValue" forFieldValues:@[]]; NSDictionary *expected = @{ - @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeStringGauge], + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeString], @"fields" : @{ @"" : @[ @{ @"value" : @"", @@ -254,7 +254,7 @@ - (void)testExportNSDictionary { } }; - XCTAssertEqualObjects([s exportNSDictionary], expected); + XCTAssertEqualObjects([s export], expected); } @end @@ -265,7 +265,7 @@ - (void)testRootLabels { NSDictionary *expected = @{@"root_labels" : @{@"hostname" : @"localhost"}, @"metrics" : @{}}; - NSDictionary *output = [metricSet exportNSDictionary]; + NSDictionary *output = [metricSet export]; XCTAssertEqualObjects(output, expected); } @@ -300,7 +300,7 @@ - (void)testRegisterCallback { }]; // ensure the callback is called. - [metricSet exportNSDictionary]; + [metricSet export]; XCTAssertEqual([gauge getGaugeValueForFieldValues:@[]], 1); } @@ -325,7 +325,7 @@ - (void)testAddConstantBool { } }; - XCTAssertEqualObjects([metricSet exportNSDictionary][@"metrics"], expected); + XCTAssertEqualObjects([metricSet export][@"metrics"], expected); } - (void)testAddConstantString { @@ -341,7 +341,6 @@ - (void)testAddConstantString { @"fields" : @{ @"" : @[ @{ @"value" : @"", - @"created" : [NSDate date], @"last_updated" : [NSDate date], @"data" : @"20210806.0.1" @@ -350,7 +349,7 @@ - (void)testAddConstantString { } }; - XCTAssertEqualObjects([metricSet exportNSDictionary][@"metrics"], expected); + XCTAssertEqualObjects([metricSet export][@"metrics"], expected); } - (void)testAddConstantInt { @@ -373,7 +372,7 @@ - (void)testAddConstantInt { } }; - XCTAssertEqualObjects([metricSet exportNSDictionary][@"metrics"], expected); + XCTAssertEqualObjects([metricSet export][@"metrics"], expected); } - (void)testExportNSDictionary { @@ -416,7 +415,7 @@ - (void)testExportNSDictionary { [metricSet int64GaugeWithName:@"/proc/memory/resident_size" fieldNames:@[] helpText:@"The resident set siz of this process."]; - + [metricSet registerCallback:^(void) { [virtualMemoryGauge set:987654321 forFieldValues:@[]]; [residentMemoryGauge set:123456789 forFieldValues:@[]]; @@ -456,7 +455,7 @@ - (void)testExportNSDictionary { }, }, @"/santa/rules" : @{ - @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeInt64Gauge], + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeInt64], @"fields" : @{ @"rule_type" : @[ @{ @@ -497,7 +496,7 @@ - (void)testExportNSDictionary { }, }, @"/proc/memory/virtual_size" : @{ - @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeInt64Gauge], + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeInt64], @"fields" : @{ @"" : @[ @{ @"value" : @"", @@ -508,7 +507,7 @@ - (void)testExportNSDictionary { } }, @"/proc/memory/resident_size" : @{ - @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeInt64Gauge], + @"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeInt64], @"fields" : @{ @"" : @[ @{ @"value" : @"", @@ -521,7 +520,7 @@ - (void)testExportNSDictionary { } }; - XCTAssertEqualObjects([metricSet exportNSDictionary], expected); + XCTAssertEqualObjects([metricSet export], expected); } @end From 73522c5355a649eac6f3329ecc1c2cd114b11982 Mon Sep 17 00:00:00 2001 From: Pete Markowsky Date: Thu, 12 Aug 2021 12:24:03 -0400 Subject: [PATCH 6/6] Addressed PR feedback. --- Source/common/SNTMetricSet.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/common/SNTMetricSet.m b/Source/common/SNTMetricSet.m index 8c4d1016e..0a90e437a 100644 --- a/Source/common/SNTMetricSet.m +++ b/Source/common/SNTMetricSet.m @@ -214,7 +214,7 @@ - (SNTMetricValue *)metricValueForFieldValues:(NSArray *)fieldValues if (!metricValue) { // Deep copy to prevent mutations to the keys we store in the dictionary. - fieldValues = [[NSArray alloc] initWithArray:fieldValues copyItems:YES]; + fieldValues = [fieldValues copy]; metricValue = [[SNTMetricValue alloc] init]; _metricsForFieldValues[fieldValues] = metricValue; }