7 #import <Foundation/Foundation.h>
17 thread_array_t threads = NULL;
18 mach_msg_type_number_t thread_count = 0;
20 MachThreads() =
default;
23 kern_return_t kernel_return_code = vm_deallocate(
24 mach_task_self(),
reinterpret_cast<vm_offset_t
>(threads), thread_count *
sizeof(thread_t));
25 FML_CHECK(kernel_return_code == KERN_SUCCESS) <<
"Failed to deallocate thread infos.";
29 FML_DISALLOW_COPY_AND_ASSIGN(MachThreads);
37 #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG || \
38 FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE
46 io_object_t ClearValue<io_object_t>() {
54 typedef void (*Deleter)(T);
55 explicit Scoped(Deleter deleter) : object_(ClearValue<T>()), deleter_(deleter) {}
56 Scoped(T
object, Deleter deleter) : object_(object), deleter_(deleter) {}
65 object_ = ClearValue<T>();
69 T get() {
return object_; }
70 void reset(T new_value) {
78 FML_DISALLOW_COPY_ASSIGN_AND_MOVE(Scoped);
83 void DeleteCF(CFMutableDictionaryRef value) {
87 void DeleteIO(io_object_t value) {
91 std::optional<GpuUsageInfo> FindGpuUsageInfo(
io_iterator_t iterator) {
92 for (Scoped<io_registry_entry_t> regEntry(
IOIteratorNext(iterator), DeleteIO); regEntry.get();
94 Scoped<CFMutableDictionaryRef> serviceDictionary(DeleteCF);
100 NSDictionary* dictionary =
101 ((__bridge NSDictionary*)serviceDictionary.get())[
@"PerformanceStatistics"];
102 NSNumber* utilization = dictionary[
@"Device Utilization %"];
104 return (GpuUsageInfo){.percent_usage = [utilization doubleValue]};
110 [[maybe_unused]] std::optional<GpuUsageInfo> FindSimulatorGpuUsageInfo() {
111 Scoped<io_iterator_t> iterator(DeleteIO);
114 return FindGpuUsageInfo(iterator.get());
119 [[maybe_unused]] std::optional<GpuUsageInfo> FindDeviceGpuUsageInfo() {
120 Scoped<io_iterator_t> iterator(DeleteIO);
123 for (Scoped<io_registry_entry_t> regEntry(
IOIteratorNext(iterator.get()), DeleteIO);
125 Scoped<io_iterator_t> innerIterator(DeleteIO);
128 std::optional<GpuUsageInfo> result = FindGpuUsageInfo(innerIterator.get());
129 if (result.has_value()) {
138 #endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG ||
141 std::optional<GpuUsageInfo> PollGpuUsage() {
142 #if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE || \
143 FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_JIT_RELEASE)
145 #elif TARGET_IPHONE_SIMULATOR
146 return FindSimulatorGpuUsageInfo();
148 return FindDeviceGpuUsageInfo();
149 #endif // TARGET_IPHONE_SIMULATOR
154 return {.cpu_usage = CpuUsage(), .memory_usage = MemoryUsage(), .gpu_usage = PollGpuUsage()};
157 std::optional<CpuUsageInfo> ProfilerMetricsIOS::CpuUsage() {
158 kern_return_t kernel_return_code;
159 MachThreads mach_threads = MachThreads();
163 task_threads(mach_task_self(), &mach_threads.threads, &mach_threads.thread_count);
164 if (kernel_return_code != KERN_SUCCESS) {
165 FML_LOG(ERROR) <<
"Error retrieving task information: "
166 << mach_error_string(kernel_return_code);
170 double total_cpu_usage = 0.0;
171 uint32_t num_threads = mach_threads.thread_count;
180 for (mach_msg_type_number_t i = 0; i < mach_threads.thread_count; i++) {
181 thread_basic_info_data_t basic_thread_info;
182 mach_msg_type_number_t thread_info_count = THREAD_BASIC_INFO_COUNT;
184 thread_info(mach_threads.threads[i], THREAD_BASIC_INFO,
185 reinterpret_cast<thread_info_t
>(&basic_thread_info), &thread_info_count);
186 switch (kernel_return_code) {
188 const double current_thread_cpu_usage =
189 basic_thread_info.cpu_usage /
static_cast<float>(TH_USAGE_SCALE);
190 total_cpu_usage += current_thread_cpu_usage;
193 case MACH_SEND_TIMEOUT:
194 case MACH_SEND_TIMED_OUT:
195 case MACH_SEND_INVALID_DEST:
203 FML_LOG(ERROR) <<
"Error retrieving thread information: "
204 << mach_error_string(kernel_return_code);
209 flutter::CpuUsageInfo cpu_usage_info = {.num_threads = num_threads,
210 .total_cpu_usage = total_cpu_usage * 100.0};
211 return cpu_usage_info;
214 std::optional<MemoryUsageInfo> ProfilerMetricsIOS::MemoryUsage() {
215 kern_return_t kernel_return_code;
216 task_vm_info_data_t task_memory_info;
217 mach_msg_type_number_t task_memory_info_count = TASK_VM_INFO_COUNT;
220 task_info(mach_task_self(), TASK_VM_INFO,
reinterpret_cast<task_info_t
>(&task_memory_info),
221 &task_memory_info_count);
222 if (kernel_return_code != KERN_SUCCESS) {
223 FML_LOG(ERROR) <<
" Error retrieving task memory information: "
224 << mach_error_string(kernel_return_code);
233 const double dirty_memory_usage =
234 static_cast<double>(task_memory_info.phys_footprint) / 1024.0 / 1024.0;
235 const double owned_shared_memory_usage =
236 static_cast<double>(task_memory_info.resident_size) / 1024.0 / 1024.0 - dirty_memory_usage;
237 flutter::MemoryUsageInfo memory_usage_info = {
238 .dirty_memory_usage = dirty_memory_usage,
239 .owned_shared_memory_usage = owned_shared_memory_usage};
240 return memory_usage_info;