Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Fixes

- fix: Normalize profiling cpu usage to percent (#7798)

## 9.10.0

### Features
Expand Down
12 changes: 11 additions & 1 deletion Sources/Sentry/SentrySystemWrapper.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
# import <mach/mach.h>
# include <thread>

@interface SentrySystemWrapper ()
+ (float)normalizeCPUUsage:(integer_t)threadCPUUsage processorCount:(long)processorCount;
@end

@implementation SentrySystemWrapper {
float processorCount;
}
Expand Down Expand Up @@ -42,6 +46,11 @@ - (SentryRAMBytes)memoryFootprintBytes:(NSError *__autoreleasing _Nullable *)err
return footprintBytes;
}

+ (float)normalizeCPUUsage:(integer_t)threadCPUUsage processorCount:(long)processorCount
{
return (static_cast<float>(threadCPUUsage) / TH_USAGE_SCALE) * 100.f / processorCount;
}

- (NSNumber *)cpuUsageWithError:(NSError **)error
{
mach_msg_type_number_t count;
Expand Down Expand Up @@ -76,7 +85,8 @@ - (NSNumber *)cpuUsageWithError:(NSError **)error
return nil;
}

usage += data.cpu_usage / processorCount;
usage += [SentrySystemWrapper normalizeCPUUsage:data.cpu_usage
processorCount:static_cast<long>(processorCount)];
}

vm_deallocate(mach_task_self(), reinterpret_cast<vm_address_t>(list), sizeof(*list) * count);
Expand Down
13 changes: 10 additions & 3 deletions Sources/Sentry/include/SentrySystemWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,19 @@ typedef mach_vm_size_t SentryRAMBytes;
- (SentryRAMBytes)memoryFootprintBytes:(NSError **)error;

/**
* @return The CPU usage per core, where the order of results corresponds to the core number as
* returned by the underlying system call, e.g. @c @[ @c <core-0-CPU-usage>, @c <core-1-CPU-usage>,
* @c ...] .
* @return The CPU usage of this process as a percentage of the device's total CPU capacity,
* normalized to a range from @c 0.0 to @c 100.0.
*/
- (nullable NSNumber *)cpuUsageWithError:(NSError **)error;

# if defined(SENTRY_TEST) || defined(SENTRY_TEST_CI)
/**
* Test-only helper that normalizes a thread CPU usage value returned by Mach to the
* process's percentage of the device's total CPU capacity.
*/
+ (float)normalizeCPUUsage:(integer_t)threadCPUUsage processorCount:(long)processorCount;
# endif

// Only some architectures support reading energy.
# if defined(__arm__) || defined(__arm64__)
/**
Expand Down
16 changes: 11 additions & 5 deletions Tests/SentryProfilerTests/SentrySystemWrapperTests.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Foundation
import XCTest

#if os(iOS) || os(macOS)
Expand All @@ -7,11 +8,16 @@ class SentrySystemWrapperTests: XCTestCase {
}
lazy private var fixture = Fixture()

func testCPUUsageReportsData() throws {
XCTAssertNoThrow({
let cpuUsage = try XCTUnwrap(self.fixture.systemWrapper.cpuUsage())
XCTAssertTrue((0.0 ... 100.0).contains(cpuUsage.doubleValue))
})
func testCPUUsage_whenIdle_shouldReportNormalizedPercent() throws {
let cpuUsage = try XCTUnwrap(fixture.systemWrapper.cpuUsage())

XCTAssertTrue((0.0 ... 100.0).contains(cpuUsage.doubleValue))
}

func testNormalizeCPUUsage_shouldConvertMachScaleToPercentOfTotalCapacity() {
XCTAssertEqual(SentrySystemWrapper.normalizeCPUUsage(1_000, processorCount: 4), 25.0, accuracy: 0.001)
XCTAssertEqual(SentrySystemWrapper.normalizeCPUUsage(500, processorCount: 8), 6.25, accuracy: 0.001)
XCTAssertEqual(SentrySystemWrapper.normalizeCPUUsage(1_000, processorCount: 10), 10.0, accuracy: 0.001)
}

func testMemoryFootprint() {
Expand Down
Loading