diff --git a/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h b/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h index d8c36cd290512..38ca90469ab9c 100644 --- a/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h +++ b/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h @@ -30,6 +30,8 @@ class ProfilerMetricsIOS { private: std::optional CpuUsage(); + std::optional MemoryUsage(); + FML_DISALLOW_COPY_AND_ASSIGN(ProfilerMetricsIOS); }; diff --git a/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm b/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm index 54a15945a3e45..db223ff2fb387 100644 --- a/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm +++ b/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm @@ -30,7 +30,7 @@ namespace flutter { ProfileSample ProfilerMetricsIOS::GenerateSample() { - return {.cpu_usage = CpuUsage()}; + return {.cpu_usage = CpuUsage(), .memory_usage = MemoryUsage()}; } std::optional ProfilerMetricsIOS::CpuUsage() { @@ -76,4 +76,33 @@ return cpu_usage_info; } +std::optional ProfilerMetricsIOS::MemoryUsage() { + kern_return_t kernel_return_code; + task_vm_info_data_t task_memory_info; + mach_msg_type_number_t task_memory_info_count = TASK_VM_INFO_COUNT; + + kernel_return_code = + task_info(mach_task_self(), TASK_VM_INFO, reinterpret_cast(&task_memory_info), + &task_memory_info_count); + if (kernel_return_code != KERN_SUCCESS) { + FML_LOG(ERROR) << " Error retrieving task memory information: " + << mach_error_string(kernel_return_code); + return std::nullopt; + } + + // `phys_footprint` is Apple's recommended way to measure app's memory usage. It provides the + // best approximate to xcode memory gauge. According to its source code explanation, the physical + // footprint mainly consists of app's internal memory data and IOKit mappings. `resident_size` + // is the total physical memory used by the app, so we simply do `resident_size - phys_footprint` + // to obtain the shared memory usage. + const double dirty_memory_usage = + static_cast(task_memory_info.phys_footprint) / 1024.0 / 1024.0; + const double owned_shared_memory_usage = + static_cast(task_memory_info.resident_size) / 1024.0 / 1024.0 - dirty_memory_usage; + flutter::MemoryUsageInfo memory_usage_info = { + .dirty_memory_usage = dirty_memory_usage, + .owned_shared_memory_usage = owned_shared_memory_usage}; + return memory_usage_info; +} + } // namespace flutter diff --git a/shell/profiling/sampling_profiler.cc b/shell/profiling/sampling_profiler.cc index 46844da9df753..1f5410fa8a12a 100644 --- a/shell/profiling/sampling_profiler.cc +++ b/shell/profiling/sampling_profiler.cc @@ -32,11 +32,11 @@ void SamplingProfiler::Start() const { void SamplingProfiler::SampleRepeatedly(fml::TimeDelta task_delay) const { profiler_task_runner_->PostDelayedTask( [profiler = this, task_delay = task_delay, sampler = sampler_]() { + // TODO(kaushikiska): consider buffering these every n seconds to + // avoid spamming the trace buffer. const ProfileSample usage = sampler(); if (usage.cpu_usage) { const auto& cpu_usage = usage.cpu_usage; - // TODO(kaushikiska): consider buffering these every n seconds to - // avoid spamming the trace buffer. std::string total_cpu_usage = std::to_string(cpu_usage->total_cpu_usage); std::string num_threads = std::to_string(cpu_usage->num_threads); @@ -44,6 +44,16 @@ void SamplingProfiler::SampleRepeatedly(fml::TimeDelta task_delay) const { "total_cpu_usage", total_cpu_usage.c_str(), "num_threads", num_threads.c_str()); } + if (usage.memory_usage) { + std::string dirty_memory_usage = + std::to_string(usage.memory_usage->dirty_memory_usage); + std::string owned_shared_memory_usage = + std::to_string(usage.memory_usage->owned_shared_memory_usage); + TRACE_EVENT_INSTANT2("flutter::profiling", "MemoryUsage", + "dirty_memory_usage", dirty_memory_usage.c_str(), + "owned_shared_memory_usage", + owned_shared_memory_usage.c_str()); + } profiler->SampleRepeatedly(task_delay); }, task_delay); diff --git a/shell/profiling/sampling_profiler.h b/shell/profiling/sampling_profiler.h index 76917abe4aedf..598443d037ac0 100644 --- a/shell/profiling/sampling_profiler.h +++ b/shell/profiling/sampling_profiler.h @@ -29,15 +29,29 @@ struct CpuUsageInfo { double total_cpu_usage; }; +/** + * @brief Memory usage stats. `dirty_memory_usage` is the the memory usage (in + * MB) such that the app uses its physical memory for dirty memory. Dirty memory + * is the memory data that cannot be paged to disk. `owned_shared_memory_usage` + * is the memory usage (in MB) such that the app uses its physicaal memory for + * shared memory, including loaded frameworks and executables. On iOS, it's + * `physical memory - dirty memory`. + */ +struct MemoryUsageInfo { + double dirty_memory_usage; + double owned_shared_memory_usage; +}; + /** * @brief Container for the metrics we collect during each run of `Sampler`. - * This currently holds `CpuUsageInfo` but the intent is to expand it to other - * metrics. + * This currently holds `CpuUsageInfo` and `MemoryUsageInfo` but the intent + * is to expand it to other metrics. * * @see flutter::Sampler */ struct ProfileSample { std::optional cpu_usage; + std::optional memory_usage; }; /**