Skip to content

Commit

Permalink
started polling the gpu usage (flutter#18752)
Browse files Browse the repository at this point in the history
  • Loading branch information
gaaclarke authored Jun 15, 2020
1 parent c00882a commit ca2a370
Show file tree
Hide file tree
Showing 6 changed files with 250 additions and 3 deletions.
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,7 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterView.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/IOKit.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm
Expand Down
9 changes: 7 additions & 2 deletions shell/platform/darwin/ios/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,18 @@ source_set("flutter_framework_source") {
public_configs = [ "//flutter:config" ]

libs = [
"AudioToolbox.framework",
"CoreMedia.framework",
"CoreVideo.framework",
"UIKit.framework",
"OpenGLES.framework",
"AudioToolbox.framework",
"QuartzCore.framework",
"UIKit.framework",
]
if (flutter_runtime_mode == "profile" || flutter_runtime_mode == "debug") {
# This is required by the profiler_metrics_ios.mm to get GPU statistics.
# Usage in release builds will cause rejection from the App Store.
libs += [ "IOKit.framework" ]
}
}

ios_test_flutter_path = rebase_path("$root_out_dir/libios_test_flutter.dylib")
Expand Down
108 changes: 108 additions & 0 deletions shell/platform/darwin/ios/framework/Source/IOKit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// These declarations are an amalgamation of different headers whose
// symbols exist in IOKit.framework. The headers have been removed
// from the iOS SDKs but all the functions are documented here:
// * https://developer.apple.com/documentation/iokit/iokitlib_h?language=objc
// * https://developer.apple.com/documentation/iokit/iokit_functions?language=objc
// * file:///Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/IOKit.framework/Versions/A/Headers/IOKitLib.h

#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG || \
FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE
#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_IOKIT_H_
#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_IOKIT_H_

#if __cplusplus
extern "C" {
#endif // __cplusplus

#include <CoreFoundation/CoreFoundation.h>
#include <mach/mach.h>
#include <stdint.h>

#define IOKIT
#include <device/device_types.h>

static const char* kIOServicePlane = "IOService";

typedef io_object_t io_registry_entry_t;
typedef io_object_t io_service_t;
typedef io_object_t io_connect_t;
typedef io_object_t io_iterator_t;

enum {
kIOReturnSuccess = 0,
};

extern const mach_port_t kIOMasterPortDefault;

kern_return_t IOObjectRetain(io_object_t object);
kern_return_t IOObjectRelease(io_object_t object);
boolean_t IOObjectConformsTo(io_object_t object, const io_name_t name);
uint32_t IOObjectGetKernelRetainCount(io_object_t object);
kern_return_t IOObjectGetClass(io_object_t object, io_name_t name);
CFStringRef IOObjectCopyClass(io_object_t object);
CFStringRef IOObjectCopySuperclassForClass(CFStringRef name);
CFStringRef IOObjectCopyBundleIdentifierForClass(CFStringRef name);

io_registry_entry_t IORegistryGetRootEntry(mach_port_t master);
kern_return_t IORegistryEntryGetName(io_registry_entry_t entry, io_name_t name);
kern_return_t IORegistryEntryGetRegistryEntryID(io_registry_entry_t entry,
uint64_t* entryID);
kern_return_t IORegistryEntryGetPath(io_registry_entry_t entry,
const io_name_t plane,
io_string_t path);
kern_return_t IORegistryEntryGetProperty(io_registry_entry_t entry,
const io_name_t name,
io_struct_inband_t buffer,
uint32_t* size);
kern_return_t IORegistryEntryCreateCFProperties(
io_registry_entry_t entry,
CFMutableDictionaryRef* properties,
CFAllocatorRef allocator,
uint32_t options);
CFTypeRef IORegistryEntryCreateCFProperty(io_registry_entry_t entry,
CFStringRef key,
CFAllocatorRef allocator,
uint32_t options);
kern_return_t IORegistryEntrySetCFProperties(io_registry_entry_t entry,
CFTypeRef properties);

kern_return_t IORegistryCreateIterator(mach_port_t master,
const io_name_t plane,
uint32_t options,
io_iterator_t* it);
kern_return_t IORegistryEntryCreateIterator(io_registry_entry_t entry,
const io_name_t plane,
uint32_t options,
io_iterator_t* it);
kern_return_t IORegistryEntryGetChildIterator(io_registry_entry_t entry,
const io_name_t plane,
io_iterator_t* it);
kern_return_t IORegistryEntryGetParentIterator(io_registry_entry_t entry,
const io_name_t plane,
io_iterator_t* it);
io_object_t IOIteratorNext(io_iterator_t it);
boolean_t IOIteratorIsValid(io_iterator_t it);
void IOIteratorReset(io_iterator_t it);

CFMutableDictionaryRef IOServiceMatching(const char* name) CF_RETURNS_RETAINED;
CFMutableDictionaryRef IOServiceNameMatching(const char* name)
CF_RETURNS_RETAINED;
io_service_t IOServiceGetMatchingService(mach_port_t master,
CFDictionaryRef matching
CF_RELEASES_ARGUMENT);
kern_return_t IOServiceGetMatchingServices(mach_port_t master,
CFDictionaryRef matching
CF_RELEASES_ARGUMENT,
io_iterator_t* it);

#if __cplusplus
}
#endif // __cplusplus

#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_IOKIT_H_
#endif // defined(FLUTTER_RUNTIME_MODE_DEBUG) ||
// defined(FLUTTER_RUNTIME_MODE_PROFILE)
121 changes: 120 additions & 1 deletion shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// found in the LICENSE file.

#include "flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h"
#import <Foundation/Foundation.h>
#import "IOKit.h"

namespace {

Expand All @@ -28,9 +30,126 @@
}

namespace flutter {
namespace {

#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG || \
FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE

template <typename T>
T ClearValue() {
return nullptr;
}

template <>
io_object_t ClearValue<io_object_t>() {
return 0;
}

template <typename T>
/// Generic RAII wrapper like unique_ptr but gives access to its handle.
class Scoped {
public:
typedef void (*Deleter)(T);
explicit Scoped(Deleter deleter) : object_(ClearValue<T>()), deleter_(deleter) {}
Scoped(T object, Deleter deleter) : object_(object), deleter_(deleter) {}
~Scoped() {
if (object_) {
deleter_(object_);
}
}
T* handle() {
if (object_) {
deleter_(object_);
object_ = ClearValue<T>();
}
return &object_;
}
T get() { return object_; }
void reset(T new_value) {
if (object_) {
deleter_(object_);
}
object_ = new_value;
}

private:
FML_DISALLOW_COPY_ASSIGN_AND_MOVE(Scoped);
T object_;
Deleter deleter_;
};

void DeleteCF(CFMutableDictionaryRef value) {
CFRelease(value);
}

void DeleteIO(io_object_t value) {
IOObjectRelease(value);
}

std::optional<GpuUsageInfo> FindGpuUsageInfo(io_iterator_t iterator) {
for (Scoped<io_registry_entry_t> regEntry(IOIteratorNext(iterator), DeleteIO); regEntry.get();
regEntry.reset(IOIteratorNext(iterator))) {
Scoped<CFMutableDictionaryRef> serviceDictionary(DeleteCF);
if (IORegistryEntryCreateCFProperties(regEntry.get(), serviceDictionary.handle(),
kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) {
continue;
}

NSDictionary* dictionary =
((__bridge NSDictionary*)serviceDictionary.get())[@"PerformanceStatistics"];
NSNumber* utilization = dictionary[@"Device Utilization %"];
if (utilization) {
return (GpuUsageInfo){.percent_usage = [utilization doubleValue]};
}
}
return std::nullopt;
}

[[maybe_unused]] std::optional<GpuUsageInfo> FindSimulatorGpuUsageInfo() {
Scoped<io_iterator_t> iterator(DeleteIO);
if (IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceNameMatching("IntelAccelerator"),
iterator.handle()) == kIOReturnSuccess) {
return FindGpuUsageInfo(iterator.get());
}
return std::nullopt;
}

[[maybe_unused]] std::optional<GpuUsageInfo> FindDeviceGpuUsageInfo() {
Scoped<io_iterator_t> iterator(DeleteIO);
if (IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceNameMatching("sgx"),
iterator.handle()) == kIOReturnSuccess) {
for (Scoped<io_registry_entry_t> regEntry(IOIteratorNext(iterator.get()), DeleteIO);
regEntry.get(); regEntry.reset(IOIteratorNext(iterator.get()))) {
Scoped<io_iterator_t> innerIterator(DeleteIO);
if (IORegistryEntryGetChildIterator(regEntry.get(), kIOServicePlane,
innerIterator.handle()) == kIOReturnSuccess) {
std::optional<GpuUsageInfo> result = FindGpuUsageInfo(innerIterator.get());
if (result.has_value()) {
return result;
}
}
}
}
return std::nullopt;
}

#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG ||
// FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE

std::optional<GpuUsageInfo> PollGpuUsage() {
#if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE || \
FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_JIT_RELEASE)
return std::nullopt;
#elif TARGET_IPHONE_SIMULATOR
return FindSimulatorGpuUsageInfo();
#elif TARGET_OS_IOS
return FindDeviceGpuUsageInfo();
#endif // TARGET_IPHONE_SIMULATOR
}
} // namespace

ProfileSample ProfilerMetricsIOS::GenerateSample() {
return {.cpu_usage = CpuUsage(), .memory_usage = MemoryUsage()};
return {.cpu_usage = CpuUsage(), .memory_usage = MemoryUsage(), .gpu_usage = PollGpuUsage()};
}

std::optional<CpuUsageInfo> ProfilerMetricsIOS::CpuUsage() {
Expand Down
6 changes: 6 additions & 0 deletions shell/profiling/sampling_profiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ void SamplingProfiler::SampleRepeatedly(fml::TimeDelta task_delay) const {
"owned_shared_memory_usage",
owned_shared_memory_usage.c_str());
}
if (usage.gpu_usage) {
std::string gpu_usage =
std::to_string(usage.gpu_usage->percent_usage);
TRACE_EVENT_INSTANT1("flutter::profiling", "GpuUsage", "gpu_usage",
gpu_usage.c_str());
}
profiler->SampleRepeatedly(task_delay);
},
task_delay);
Expand Down
8 changes: 8 additions & 0 deletions shell/profiling/sampling_profiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ struct MemoryUsageInfo {
double owned_shared_memory_usage;
};

/**
* @brief Polled information related to the usage of the GPU.
*/
struct GpuUsageInfo {
double percent_usage;
};

/**
* @brief Container for the metrics we collect during each run of `Sampler`.
* This currently holds `CpuUsageInfo` and `MemoryUsageInfo` but the intent
Expand All @@ -52,6 +59,7 @@ struct MemoryUsageInfo {
struct ProfileSample {
std::optional<CpuUsageInfo> cpu_usage;
std::optional<MemoryUsageInfo> memory_usage;
std::optional<GpuUsageInfo> gpu_usage;
};

/**
Expand Down

0 comments on commit ca2a370

Please sign in to comment.