Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: measure cpu energy usage estimates #3217

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
armcknight marked this conversation as resolved.
Show resolved Hide resolved
}

@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