From f6b143160cfaf10cf62c16880dc8337a33fb07ec Mon Sep 17 00:00:00 2001 From: albertbow Date: Tue, 19 May 2020 15:53:32 -0700 Subject: [PATCH 1/6] Add iOS memory profiling to Flutter profiling. --- .../framework/Source/profiler_metrics_ios.h | 2 ++ .../framework/Source/profiler_metrics_ios.mm | 26 ++++++++++++++++++- shell/profiling/sampling_profiler.cc | 11 ++++++++ shell/profiling/sampling_profiler.h | 18 +++++++++++-- 4 files changed, 54 insertions(+), 3 deletions(-) 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..ef988e2e125d6 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,28 @@ 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; + } + + const double dirty_memory_usage = + static_cast(task_memory_info.phys_footprint) / 1024.0 / 1024.0; + const double owned_share_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_share_memory_usage = owned_share_memory_usage}; + return memory_usage_info; +} + } // namespace flutter diff --git a/shell/profiling/sampling_profiler.cc b/shell/profiling/sampling_profiler.cc index 46844da9df753..fd79b76610958 100644 --- a/shell/profiling/sampling_profiler.cc +++ b/shell/profiling/sampling_profiler.cc @@ -44,6 +44,17 @@ 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) { + // TODO(kaushikiska): consider buffering these every n seconds to + // avoid spamming the trace buffer. + std::string dirty_memory_usage = + std::to_string(usage.memory_usage->dirty_memory_usage); + std::string owned_share_memory_usage = + std::to_string(usage.memory_usage->owned_share_memory_usage); + TRACE_EVENT_INSTANT2("flutter::profiling", "MemoryUsage", + "dirty_memory_usage", dirty_memory_usage.c_str(), + "owned_share_memory_usage", owned_share_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..30310e4feff49 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 number of mega bytes + * such that the app uses its physical memory for dirty memory. Dirty memory + * is the memory data that cannot be paged to disk. `owned_share_memory_usage` + * is the number of mega bytes 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_share_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; }; /** From bb431f062b622d780db96e1b2980bc45918b76b1 Mon Sep 17 00:00:00 2001 From: albertbow Date: Tue, 19 May 2020 16:12:34 -0700 Subject: [PATCH 2/6] Adjust docstrings. --- .../darwin/ios/framework/Source/profiler_metrics_ios.mm | 5 +++++ shell/profiling/sampling_profiler.cc | 6 ++---- 2 files changed, 7 insertions(+), 4 deletions(-) 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 ef988e2e125d6..92f1319a2a779 100644 --- a/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm +++ b/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm @@ -90,6 +90,11 @@ 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_share_memory_usage = diff --git a/shell/profiling/sampling_profiler.cc b/shell/profiling/sampling_profiler.cc index fd79b76610958..c69eff6dc0e74 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); @@ -45,8 +45,6 @@ void SamplingProfiler::SampleRepeatedly(fml::TimeDelta task_delay) const { "num_threads", num_threads.c_str()); } if (usage.memory_usage) { - // TODO(kaushikiska): consider buffering these every n seconds to - // avoid spamming the trace buffer. std::string dirty_memory_usage = std::to_string(usage.memory_usage->dirty_memory_usage); std::string owned_share_memory_usage = From 26c2e9ae652ed41988d22ed8857cd0d99a5a5edc Mon Sep 17 00:00:00 2001 From: albertbow Date: Tue, 19 May 2020 16:39:32 -0700 Subject: [PATCH 3/6] format. --- .../ios/framework/Source/profiler_metrics_ios.mm | 10 +++++----- shell/profiling/sampling_profiler.cc | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) 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 92f1319a2a779..83b240b54918c 100644 --- a/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm +++ b/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm @@ -82,8 +82,8 @@ 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); + 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); @@ -99,9 +99,9 @@ static_cast(task_memory_info.phys_footprint) / 1024.0 / 1024.0; const double owned_share_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_share_memory_usage = owned_share_memory_usage}; + flutter::MemoryUsageInfo memory_usage_info = { + .dirty_memory_usage = dirty_memory_usage, + .owned_share_memory_usage = owned_share_memory_usage}; return memory_usage_info; } diff --git a/shell/profiling/sampling_profiler.cc b/shell/profiling/sampling_profiler.cc index c69eff6dc0e74..5f07377ff90d5 100644 --- a/shell/profiling/sampling_profiler.cc +++ b/shell/profiling/sampling_profiler.cc @@ -51,7 +51,8 @@ void SamplingProfiler::SampleRepeatedly(fml::TimeDelta task_delay) const { std::to_string(usage.memory_usage->owned_share_memory_usage); TRACE_EVENT_INSTANT2("flutter::profiling", "MemoryUsage", "dirty_memory_usage", dirty_memory_usage.c_str(), - "owned_share_memory_usage", owned_share_memory_usage.c_str()); + "owned_share_memory_usage", + owned_share_memory_usage.c_str()); } profiler->SampleRepeatedly(task_delay); }, From 1b8a3ddd875a72010166c4d966d2475bf1f656db Mon Sep 17 00:00:00 2001 From: albertbow Date: Wed, 20 May 2020 10:52:34 -0700 Subject: [PATCH 4/6] Module renaming: owned_share_memory_usage -> owned_shared_memory_usage. --- .../darwin/ios/framework/Source/profiler_metrics_ios.mm | 4 ++-- shell/profiling/sampling_profiler.cc | 8 ++++---- shell/profiling/sampling_profiler.h | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) 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 83b240b54918c..a470669b87746 100644 --- a/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm +++ b/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm @@ -97,11 +97,11 @@ // 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_share_memory_usage = + 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_share_memory_usage = owned_share_memory_usage}; + .owned_shared_memory_usage = owned_shared_memory_usage}; return memory_usage_info; } diff --git a/shell/profiling/sampling_profiler.cc b/shell/profiling/sampling_profiler.cc index 5f07377ff90d5..1f5410fa8a12a 100644 --- a/shell/profiling/sampling_profiler.cc +++ b/shell/profiling/sampling_profiler.cc @@ -47,12 +47,12 @@ void SamplingProfiler::SampleRepeatedly(fml::TimeDelta task_delay) const { if (usage.memory_usage) { std::string dirty_memory_usage = std::to_string(usage.memory_usage->dirty_memory_usage); - std::string owned_share_memory_usage = - std::to_string(usage.memory_usage->owned_share_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_share_memory_usage", - owned_share_memory_usage.c_str()); + "owned_shared_memory_usage", + owned_shared_memory_usage.c_str()); } profiler->SampleRepeatedly(task_delay); }, diff --git a/shell/profiling/sampling_profiler.h b/shell/profiling/sampling_profiler.h index 30310e4feff49..6888255553540 100644 --- a/shell/profiling/sampling_profiler.h +++ b/shell/profiling/sampling_profiler.h @@ -32,14 +32,14 @@ struct CpuUsageInfo { /** * @brief Memory usage stats. `dirty_memory_usage` is the number of mega bytes * such that the app uses its physical memory for dirty memory. Dirty memory - * is the memory data that cannot be paged to disk. `owned_share_memory_usage` + * is the memory data that cannot be paged to disk. `owned_shared_memory_usage` * is the number of mega bytes 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_share_memory_usage; + double owned_shared_memory_usage; }; /** From 338ee30baaccec1c337df142df96238e78519633 Mon Sep 17 00:00:00 2001 From: albertbow Date: Wed, 20 May 2020 12:48:14 -0700 Subject: [PATCH 5/6] Empahsize "MB" in docstring. --- shell/profiling/sampling_profiler.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/profiling/sampling_profiler.h b/shell/profiling/sampling_profiler.h index 6888255553540..598443d037ac0 100644 --- a/shell/profiling/sampling_profiler.h +++ b/shell/profiling/sampling_profiler.h @@ -30,10 +30,10 @@ struct CpuUsageInfo { }; /** - * @brief Memory usage stats. `dirty_memory_usage` is the number of mega bytes - * such that the app uses its physical memory for dirty memory. Dirty memory + * @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 number of mega bytes such that the app uses its physicaal memory for + * 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`. */ From 1c8445f48d10b594357ff5a73261445996953231 Mon Sep 17 00:00:00 2001 From: albertbow Date: Wed, 20 May 2020 14:19:00 -0700 Subject: [PATCH 6/6] float -> double for type consistency. --- .../darwin/ios/framework/Source/profiler_metrics_ios.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 a470669b87746..db223ff2fb387 100644 --- a/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm +++ b/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm @@ -96,9 +96,9 @@ // 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; + 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; + 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};