Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
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
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,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 = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm surprised that out GN format presubmit doesn't account for these.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, that formatter is usually pretty strict!

"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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has this file been copied from somewhere? If so could you add a comment pointing to the source or reference? Reading it seems like it's an accumulation of a bunch of IOKit headers into one file.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, added documentation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does copyright work when the header is an amalgamation of other (presumably copyrighted) headers?

// 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 || \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given how big this class is getting, consider moving this to gpu_profiler_metrics_ios.<h|mm>.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file is 228 lines, shrug. If you feel strongly about it I can move it. It's an easy change, just takes time with our current system.

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