Skip to content

Commit

Permalink
feat: measure cpu energy usage estimates (#3217)
Browse files Browse the repository at this point in the history
  • Loading branch information
armcknight authored Aug 16, 2023
1 parent 486641c commit 4beeeff
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## Unreleased

### Features

- Record energy usage estimates for profiling (#3217)

## 8.9.4

Expand Down
36 changes: 36 additions & 0 deletions Sources/Sentry/SentryMetricProfiler.mm
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ @implementation SentryMetricReading

NSString *const kSentryMetricProfilerSerializationKeyMemoryFootprint = @"memory_footprint";
NSString *const kSentryMetricProfilerSerializationKeyCPUUsageFormat = @"cpu_usage_%d";
NSString *const kSentryMetricProfilerSerializationKeyCPUEnergyUsage = @"cpu_energy_usage";

NSString *const kSentryMetricProfilerSerializationUnitBytes = @"byte";
NSString *const kSentryMetricProfilerSerializationUnitPercentage = @"percent";
Expand Down Expand Up @@ -82,6 +83,9 @@ @implementation SentryMetricProfiler {
NSMutableDictionary<NSNumber *, NSMutableArray<SentryMetricReading *> *> *_cpuUsage;

NSMutableArray<SentryMetricReading *> *_memoryFootprint;

NSNumber *previousEnergyReading;
NSMutableArray<SentryMetricReading *> *_cpuEnergyUsage;
}

- (instancetype)init
Expand All @@ -98,6 +102,7 @@ - (instancetype)init
}

_memoryFootprint = [NSMutableArray<SentryMetricReading *> array];
_cpuEnergyUsage = [NSMutableArray<SentryMetricReading *> array];
}
return self;
}
Expand All @@ -123,8 +128,10 @@ - (void)stop
and:(uint64_t)endSystemTime;
{
NSArray<SentryMetricReading *> *memoryFootprint;
NSArray<SentryMetricReading *> *cpuEnergyUsage;
NSDictionary<NSNumber *, NSArray<SentryMetricReading *> *> *cpuUsage;
@synchronized(self) {
cpuEnergyUsage = [NSArray<SentryMetricReading *> arrayWithArray:_cpuEnergyUsage];
memoryFootprint = [NSArray<SentryMetricReading *> arrayWithArray:_memoryFootprint];
cpuUsage = [NSDictionary<NSNumber *, NSArray<SentryMetricReading *> *>
dictionaryWithDictionary:_cpuUsage];
Expand All @@ -136,6 +143,11 @@ - (void)stop
= serializeValuesWithNormalizedTime(memoryFootprint,
kSentryMetricProfilerSerializationUnitBytes, startSystemTime, endSystemTime);
}
if (cpuEnergyUsage.count > 0) {
dict[kSentryMetricProfilerSerializationKeyCPUEnergyUsage]
= serializeValuesWithNormalizedTime(cpuEnergyUsage,
kSentryMetricProfilerSerializationUnitBytes, startSystemTime, endSystemTime);
}

[cpuUsage enumerateKeysAndObjectsUsingBlock:^(NSNumber *_Nonnull core,
NSArray<SentryMetricReading *> *_Nonnull readings, BOOL *_Nonnull stop) {
Expand Down Expand Up @@ -167,6 +179,7 @@ - (void)registerSampler
eventHandler:^{
[weakSelf recordCPUPercentagePerCore];
[weakSelf recordMemoryFootprint];
[weakSelf recordEnergyUsageEstimate];
}];
}

Expand Down Expand Up @@ -205,6 +218,29 @@ - (void)recordCPUPercentagePerCore
}
}

- (void)recordEnergyUsageEstimate
{
NSError *error;
const auto reading =
[SentryDependencyContainer.sharedInstance.systemWrapper cpuEnergyUsageWithError:&error];
if (error) {
SENTRY_LOG_ERROR(@"Failed to read CPU energy usage: %@", error);
return;
}

if (previousEnergyReading == nil) {
previousEnergyReading = reading;
return;
}

const auto value = reading.unsignedIntegerValue - previousEnergyReading.unsignedIntegerValue;
previousEnergyReading = reading;

@synchronized(self) {
[_cpuEnergyUsage addObject:[self metricReadingForValue:@(value)]];
}
}

- (SentryMetricReading *)metricReadingForValue:(NSNumber *)value
{
const auto reading = [[SentryMetricReading alloc] init];
Expand Down
18 changes: 18 additions & 0 deletions Sources/Sentry/SentrySystemWrapper.mm
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,22 @@ - (SentryRAMBytes)memoryFootprintBytes:(NSError *__autoreleasing _Nullable *)err
return result;
}

- (NSNumber *)cpuEnergyUsageWithError:(NSError **)error
{
struct task_power_info_v2 powerInfo;

mach_msg_type_number_t size = TASK_POWER_INFO_V2_COUNT;

task_t task = mach_task_self();
kern_return_t kr = task_info(task, TASK_POWER_INFO_V2, (task_info_t)&powerInfo, &size);
if (kr != KERN_SUCCESS) {
if (error) {
*error = NSErrorFromSentryErrorWithKernelError(
kSentryErrorKernel, @"Error with task_info(…TASK_POWER_INFO_V2…).", kr);
;
}
}
return @(powerInfo.cpu_energy.total_system + powerInfo.cpu_energy.total_user);
}

@end
1 change: 1 addition & 0 deletions Sources/Sentry/include/SentryMetricProfiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ NS_ASSUME_NONNULL_BEGIN

SENTRY_EXTERN NSString *const kSentryMetricProfilerSerializationKeyMemoryFootprint;
SENTRY_EXTERN NSString *const kSentryMetricProfilerSerializationKeyCPUUsageFormat;
SENTRY_EXTERN NSString *const kSentryMetricProfilerSerializationKeyCPUEnergyUsage;

SENTRY_EXTERN NSString *const kSentryMetricProfilerSerializationUnitBytes;
SENTRY_EXTERN NSString *const kSentryMetricProfilerSerializationUnitPercentage;
Expand Down
6 changes: 6 additions & 0 deletions Sources/Sentry/include/SentrySystemWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ typedef mach_vm_size_t SentryRAMBytes;
*/
- (nullable NSArray<NSNumber *> *)cpuUsagePerCore:(NSError **)error;

/**
* @return The cumulative amount of nanojoules expended by the CPU for this task since process
* start.
*/
- (nullable NSNumber *)cpuEnergyUsageWithError:(NSError **)error;

@end

NS_ASSUME_NONNULL_END

0 comments on commit 4beeeff

Please sign in to comment.