132 lines
4.6 KiB
Objective-C
132 lines
4.6 KiB
Objective-C
#import "MMKernelTelemetryProvider.h"
|
|
#import <mach/mach.h>
|
|
#import <mach/mach_time.h>
|
|
#import <libproc.h>
|
|
#import <os/lock.h>
|
|
|
|
@interface MMKernelTelemetryProvider () {
|
|
os_unfair_lock _lock;
|
|
NSDictionary<NSString *, NSNumber *> *_previousCounters;
|
|
uint64_t _previousTimestamp;
|
|
mach_timebase_info_data_t _timebase;
|
|
}
|
|
@end
|
|
|
|
@implementation MMKernelTelemetryProvider
|
|
|
|
- (instancetype)init {
|
|
self = [super init];
|
|
if (self) {
|
|
_lock = OS_UNFAIR_LOCK_INIT;
|
|
_previousCounters = nil;
|
|
_previousTimestamp = 0;
|
|
mach_timebase_info(&_timebase);
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (MMTelemetryDomain)domain {
|
|
return MMTelemetryDomainSystem;
|
|
}
|
|
|
|
- (NSString *)providerIdentifier {
|
|
return @"com.i3omb.macmonitor.telemetry.kernel.counters";
|
|
}
|
|
|
|
- (BOOL)isAvailable {
|
|
return YES;
|
|
}
|
|
|
|
+ (NSDictionary<NSString *, id> *)calculateRatesWithCurrentCounters:(NSDictionary<NSString *, NSNumber *> *)current
|
|
previousCounters:(nullable NSDictionary<NSString *, NSNumber *> *)previous
|
|
timeDelta:(NSTimeInterval)timeDelta {
|
|
NSMutableDictionary<NSString *, id> *result = [NSMutableDictionary dictionary];
|
|
|
|
// Copy all current cumulative counters into result
|
|
[current enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSNumber *val, BOOL *stop) {
|
|
result[[@"cumulative_" stringByAppendingString:key]] = val;
|
|
}];
|
|
|
|
NSArray<NSString *> *rateKeys = @[
|
|
@"contextSwitches", @"syscalls", @"pageFaults", @"cowFaults",
|
|
@"zeroFills", @"pageins", @"pageouts", @"decompressions", @"compressions"
|
|
];
|
|
|
|
for (NSString *key in rateKeys) {
|
|
double currentVal = [current[key] doubleValue];
|
|
double prevVal = previous ? [previous[key] doubleValue] : currentVal;
|
|
double delta = (currentVal >= prevVal) ? (currentVal - prevVal) : 0.0;
|
|
double rate = (timeDelta > 0.0001) ? (delta / timeDelta) : 0.0;
|
|
result[[key stringByAppendingString:@"Rate"]] = @(rate);
|
|
}
|
|
|
|
result[@"timeDelta"] = @(timeDelta);
|
|
return result;
|
|
}
|
|
|
|
- (nullable NSDictionary<NSString *, id> *)sampleTelemetryWithError:(NSError **)error {
|
|
uint64_t now = mach_absolute_time();
|
|
|
|
// 1. Query Mach VM Statistics for page faults, COW, zero fills, pageins/outs
|
|
vm_statistics64_data_t vmStats;
|
|
mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
|
|
kern_return_t kr = host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t)&vmStats, &count);
|
|
if (kr != KERN_SUCCESS) {
|
|
if (error) {
|
|
*error = [NSError errorWithDomain:@"com.i3omb.macmonitor.kernel"
|
|
code:kr
|
|
userInfo:@{NSLocalizedDescriptionKey: @"Failed to query host_statistics64"}];
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
// 2. Query process table for cumulative context switches and system calls
|
|
int pids[4096];
|
|
int bytes = proc_listpids(PROC_ALL_PIDS, 0, pids, sizeof(pids));
|
|
int pidCount = bytes / sizeof(int);
|
|
|
|
uint64_t totalCSW = 0;
|
|
uint64_t totalSyscalls = 0;
|
|
|
|
for (int i = 0; i < pidCount; i++) {
|
|
if (pids[i] <= 0) continue;
|
|
struct proc_taskinfo ti;
|
|
if (proc_pidinfo(pids[i], PROC_PIDTASKINFO, 0, &ti, sizeof(ti)) == sizeof(ti)) {
|
|
totalCSW += ti.pti_csw;
|
|
totalSyscalls += (uint64_t)ti.pti_syscalls_unix + (uint64_t)ti.pti_syscalls_mach;
|
|
}
|
|
}
|
|
|
|
NSDictionary<NSString *, NSNumber *> *currentCounters = @{
|
|
@"contextSwitches": @(totalCSW),
|
|
@"syscalls": @(totalSyscalls),
|
|
@"pageFaults": @(vmStats.faults),
|
|
@"cowFaults": @(vmStats.cow_faults),
|
|
@"zeroFills": @(vmStats.zero_fill_count),
|
|
@"pageins": @(vmStats.pageins),
|
|
@"pageouts": @(vmStats.pageouts),
|
|
@"decompressions": @(vmStats.decompressions),
|
|
@"compressions": @(vmStats.compressions)
|
|
};
|
|
|
|
os_unfair_lock_lock(&_lock);
|
|
NSDictionary<NSString *, NSNumber *> *prev = _previousCounters;
|
|
uint64_t prevTime = _previousTimestamp;
|
|
|
|
_previousCounters = currentCounters;
|
|
_previousTimestamp = now;
|
|
os_unfair_lock_unlock(&_lock);
|
|
|
|
NSTimeInterval timeDelta = 1.0;
|
|
if (prevTime > 0) {
|
|
uint64_t elapsed = now - prevTime;
|
|
timeDelta = (double)elapsed * _timebase.numer / _timebase.denom / 1e9;
|
|
}
|
|
|
|
return [MMKernelTelemetryProvider calculateRatesWithCurrentCounters:currentCounters
|
|
previousCounters:prev
|
|
timeDelta:timeDelta];
|
|
}
|
|
|
|
@end
|