Skip to content

Commit

Permalink
Merge 8a87e63 into 459ae5e
Browse files Browse the repository at this point in the history
  • Loading branch information
armcknight authored Dec 6, 2022
2 parents 459ae5e + 8a87e63 commit befe418
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 0 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Features

- Gather profiling timeseries metrics for CPU usage and memory footprint, and thermal and memory pressure events (#2493)

## 8.0.0-beta.4

This version adds a dependency on Swift.
Expand Down
8 changes: 8 additions & 0 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,8 @@
8419C0C428C1889D001C8259 /* SentryProfilerSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8419C0C328C1889D001C8259 /* SentryProfilerSwiftTests.swift */; };
8453421228BE855D00C22EEC /* SentrySampleDecision.m in Sources */ = {isa = PBXBuildFile; fileRef = 8453421128BE855D00C22EEC /* SentrySampleDecision.m */; };
8453421628BE8A9500C22EEC /* SentrySpanStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 8453421528BE8A9500C22EEC /* SentrySpanStatus.m */; };
8454CF8C293EAF9A006AC140 /* SentryMetricProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8454CF8A293EAF9A006AC140 /* SentryMetricProfiler.h */; };
8454CF8D293EAF9A006AC140 /* SentryMetricProfiler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8454CF8B293EAF9A006AC140 /* SentryMetricProfiler.mm */; };
84A888FD28D9B11700C51DFD /* SentryProfiler+Test.h in Headers */ = {isa = PBXBuildFile; fileRef = 84A888FC28D9B11700C51DFD /* SentryProfiler+Test.h */; };
84A8891C28DBD28900C51DFD /* SentryDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 84A8891A28DBD28900C51DFD /* SentryDevice.h */; };
84A8891D28DBD28900C51DFD /* SentryDevice.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84A8891B28DBD28900C51DFD /* SentryDevice.mm */; };
Expand Down Expand Up @@ -1412,6 +1414,8 @@
844DA81F28246DE300E6B62E /* scripts */ = {isa = PBXFileReference; lastKnownFileType = folder; path = scripts; sourceTree = "<group>"; };
8453421128BE855D00C22EEC /* SentrySampleDecision.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentrySampleDecision.m; sourceTree = "<group>"; };
8453421528BE8A9500C22EEC /* SentrySpanStatus.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentrySpanStatus.m; sourceTree = "<group>"; };
8454CF8A293EAF9A006AC140 /* SentryMetricProfiler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryMetricProfiler.h; path = Sources/Sentry/SentryMetricProfiler.h; sourceTree = SOURCE_ROOT; };
8454CF8B293EAF9A006AC140 /* SentryMetricProfiler.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = SentryMetricProfiler.mm; path = Sources/Sentry/SentryMetricProfiler.mm; sourceTree = SOURCE_ROOT; };
84A888FC28D9B11700C51DFD /* SentryProfiler+Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SentryProfiler+Test.h"; path = "Sources/Sentry/include/SentryProfiler+Test.h"; sourceTree = SOURCE_ROOT; };
84A8891A28DBD28900C51DFD /* SentryDevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryDevice.h; path = include/SentryDevice.h; sourceTree = "<group>"; };
84A8891B28DBD28900C51DFD /* SentryDevice.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryDevice.mm; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2803,6 +2807,8 @@
03F84D1B27DD414C008FE43F /* SentryMachLogging.hpp */,
03F84D2C27DD4191008FE43F /* SentryMachLogging.cpp */,
03F84D1127DD414C008FE43F /* SentryProfiler.h */,
8454CF8A293EAF9A006AC140 /* SentryMetricProfiler.h */,
8454CF8B293EAF9A006AC140 /* SentryMetricProfiler.mm */,
84A888FC28D9B11700C51DFD /* SentryProfiler+Test.h */,
03F84D2B27DD4191008FE43F /* SentryProfiler.mm */,
03BCC38D27E2A377003232C7 /* SentryProfilingConditionals.h */,
Expand Down Expand Up @@ -3098,6 +3104,7 @@
63FE711F20DA4C1000CDBAE8 /* SentryCrashObjC.h in Headers */,
7BC3936825B1AB3E004F03D3 /* SentryLevelMapper.h in Headers */,
8E4E7C6E25DAAAFE006AB9E2 /* SentrySpan.h in Headers */,
8454CF8C293EAF9A006AC140 /* SentryMetricProfiler.h in Headers */,
D8ACE3CE2762187D00F5A213 /* SentryNSDataTracker.h in Headers */,
03F84D2427DD414C008FE43F /* SentryCompiler.h in Headers */,
631E6D331EBC679C00712345 /* SentryQueueableRequestManager.h in Headers */,
Expand Down Expand Up @@ -3563,6 +3570,7 @@
7BD729982463E93500EA3610 /* SentryDateUtil.m in Sources */,
639FCF9D1EBC7F9500778193 /* SentryThread.m in Sources */,
8E8C57A225EEFC07001CEEFA /* SentryTracesSampler.m in Sources */,
8454CF8D293EAF9A006AC140 /* SentryMetricProfiler.mm in Sources */,
63FE714120DA4C1100CDBAE8 /* SentryCrashDate.c in Sources */,
63FE70DB20DA4C1000CDBAE8 /* SentryCrashMonitor_System.m in Sources */,
7BA61CBB247BC5D800C130A8 /* SentryCrashDefaultBinaryImageProvider.m in Sources */,
Expand Down
24 changes: 24 additions & 0 deletions Sources/Sentry/SentryMetricProfiler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#import <Foundation/Foundation.h>

@class SentryNSNotificationCenterWrapper;

NS_ASSUME_NONNULL_BEGIN

/**
* A profiler that gathers various time-series and event-based metrics on the app process, such as
* CPU and memory usage timeseries and thermal and memory pressure warning notifications.
*/
@interface SentryMetricProfiler : NSObject

- (instancetype)initWithNotificationCenterWrapper:
(SentryNSNotificationCenterWrapper *)notificationCenterWrapper
profileStartTime:(uint64_t)profileStartTime;
- (void)start;
- (void)stop;

/** @return All data gathered during the profiling run. */
- (NSData *)serialize;

@end

NS_ASSUME_NONNULL_END
156 changes: 156 additions & 0 deletions Sources/Sentry/SentryMetricProfiler.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#import "SentryMetricProfiler.h"
#import "SentryMachLogging.hpp"
#import "SentryNSNotificationCenterWrapper.h"
#import "SentryTime.h"
#include <mach/mach.h>

const NSTimeInterval kSentryMetricProfilerInterval = 0.1; // 10 Hz

@implementation SentryMetricProfiler {
NSTimer *_timer;
SentryNSNotificationCenterWrapper *_notificationCenter;
dispatch_source_t _memoryWarningSource;
dispatch_queue_t _memoryWarningQueue;
NSMutableArray<NSDictionary<NSString *, NSNumber *> *> *_cpuTimeSeries;
NSMutableArray<NSDictionary<NSString *, NSNumber *> *> *_memoryFootprintTimeSeries;
NSMutableArray<NSDictionary<NSString *, NSNumber *> *> *_thermalStateChanges;
NSMutableArray<NSDictionary<NSString *, NSNumber *> *> *_memoryPressureStateChanges;
uint64_t _profileStartTime;
}

- (instancetype)initWithNotificationCenterWrapper:
(SentryNSNotificationCenterWrapper *)notificationCenterWrapper
profileStartTime:(uint64_t)profileStartTime
{
if (self = [super init]) {
_cpuTimeSeries = [NSMutableArray<NSDictionary<NSString *, NSNumber *> *> array];
_memoryFootprintTimeSeries = [NSMutableArray<NSDictionary<NSString *, NSNumber *> *> array];
_thermalStateChanges = [NSMutableArray<NSDictionary<NSString *, NSNumber *> *> array];
_memoryPressureStateChanges =
[NSMutableArray<NSDictionary<NSString *, NSNumber *> *> array];
_notificationCenter = notificationCenterWrapper;
_profileStartTime = profileStartTime;
}
return self;
}

- (void)dealloc
{
[self stop];
}

#pragma mark - Public

- (void)start
{
[self registerTimeSeriesHandler];
[self registerMemoryPressureWarningHandler];
}

- (void)stop
{
[_timer invalidate];
dispatch_source_cancel(_memoryWarningSource);
[_notificationCenter removeObserver:self name:NSProcessInfoThermalStateDidChangeNotification];
}

- (NSData *)serialize
{
// TODO: implement
return [[NSData alloc] init];
}

#pragma mark - Private

- (void)registerTimeSeriesHandler
{
_timer = [NSTimer scheduledTimerWithTimeInterval:kSentryMetricProfilerInterval
repeats:YES
block:^(NSTimer *_Nonnull timer) {
[self recordCPUPercentage];
[self recordMemoryFootprint];
}];
}

/**
* This is a more fine-grained API, providing normal/warn/critical levels of memory usage, versus
* using `UIApplicationDidReceiveMemoryWarningNotification` which does not provide any additional
* information ("This notification does not contain a userInfo dictionary." from
* https://developer.apple.com/documentation/uikit/uiapplication/1622920-didreceivememorywarningnotificat).
*/
- (void)registerMemoryPressureWarningHandler
{
const auto queueAttributes
= dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0);
_memoryWarningQueue = dispatch_queue_create("io.sentry.queue.memory-warnings", queueAttributes);
_memoryWarningSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, 0,
DISPATCH_MEMORYPRESSURE_NORMAL | DISPATCH_MEMORYPRESSURE_WARN
| DISPATCH_MEMORYPRESSURE_CRITICAL,
_memoryWarningQueue);
dispatch_source_set_event_handler(_memoryWarningSource, ^{
[self recordMemoryPressureState:dispatch_source_get_data(self->_memoryWarningSource)];
});
dispatch_resume(_memoryWarningSource);
}

- (void)registerStateChangeNotifications
{
// According to Apple docs: "To receive NSProcessInfoThermalStateDidChangeNotification, you must
// access the thermalState prior to registering for the notification." (from
// https://developer.apple.com/documentation/foundation/nsprocessinfothermalstatedidchangenotification/)
[self recordThermalState];

// According to Apple docs: "This notification is posted on the global dispatch queue."
[_notificationCenter addObserver:self
selector:@selector(handleThermalStateChangeNotification:)
name:NSProcessInfoThermalStateDidChangeNotification];
}

- (void)handleThermalStateChangeNotification:(NSNotification *)note
{
[self recordThermalState];
}

- (void)recordThermalState
{
[_thermalStateChanges
addObject:[self metricEntryForValue:@(NSProcessInfo.processInfo.thermalState)]];
}

- (void)recordMemoryPressureState:(uintptr_t)memoryPressureState
{
[_memoryPressureStateChanges addObject:[self metricEntryForValue:@(memoryPressureState)]];
}

- (void)recordMemoryFootprint
{
task_vm_info_data_t info;
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
if (SENTRY_PROF_LOG_KERN_RETURN(
task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&info, &count))
== KERN_SUCCESS) {
mach_vm_size_t footprintBytes;
if (count >= TASK_VM_INFO_REV1_COUNT) {
footprintBytes = info.phys_footprint;
} else {
footprintBytes = info.resident_size;
}

[_memoryFootprintTimeSeries addObject:[self metricEntryForValue:@(footprintBytes)]];
}
}

- (void)recordCPUPercentage
{
// TODO: implement
}

- (NSDictionary<NSString *, NSNumber *> *)metricEntryForValue:(NSNumber *)value
{
return @{
@"value" : value,
@"elapsed_since_start_ns" : @(getDurationNs(_profileStartTime, getAbsoluteTime()))
};
}

@end

0 comments on commit befe418

Please sign in to comment.