Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 2187523

Browse files
[profiling] Memory Profiling support for iOS (#18516)
Based on Kaushik's work for iOS CPU profiling, I added the memory profiling within the same scheduled task. The memory profiling methodologies were discussed in the internal doc. Please see go/flutter-ios-memory-profiling.
1 parent 37d96fe commit 2187523

File tree

4 files changed

+60
-5
lines changed

4 files changed

+60
-5
lines changed

shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ class ProfilerMetricsIOS {
3030
private:
3131
std::optional<CpuUsageInfo> CpuUsage();
3232

33+
std::optional<MemoryUsageInfo> MemoryUsage();
34+
3335
FML_DISALLOW_COPY_AND_ASSIGN(ProfilerMetricsIOS);
3436
};
3537

shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
namespace flutter {
3131

3232
ProfileSample ProfilerMetricsIOS::GenerateSample() {
33-
return {.cpu_usage = CpuUsage()};
33+
return {.cpu_usage = CpuUsage(), .memory_usage = MemoryUsage()};
3434
}
3535

3636
std::optional<CpuUsageInfo> ProfilerMetricsIOS::CpuUsage() {
@@ -76,4 +76,33 @@
7676
return cpu_usage_info;
7777
}
7878

79+
std::optional<MemoryUsageInfo> ProfilerMetricsIOS::MemoryUsage() {
80+
kern_return_t kernel_return_code;
81+
task_vm_info_data_t task_memory_info;
82+
mach_msg_type_number_t task_memory_info_count = TASK_VM_INFO_COUNT;
83+
84+
kernel_return_code =
85+
task_info(mach_task_self(), TASK_VM_INFO, reinterpret_cast<task_info_t>(&task_memory_info),
86+
&task_memory_info_count);
87+
if (kernel_return_code != KERN_SUCCESS) {
88+
FML_LOG(ERROR) << " Error retrieving task memory information: "
89+
<< mach_error_string(kernel_return_code);
90+
return std::nullopt;
91+
}
92+
93+
// `phys_footprint` is Apple's recommended way to measure app's memory usage. It provides the
94+
// best approximate to xcode memory gauge. According to its source code explanation, the physical
95+
// footprint mainly consists of app's internal memory data and IOKit mappings. `resident_size`
96+
// is the total physical memory used by the app, so we simply do `resident_size - phys_footprint`
97+
// to obtain the shared memory usage.
98+
const double dirty_memory_usage =
99+
static_cast<double>(task_memory_info.phys_footprint) / 1024.0 / 1024.0;
100+
const double owned_shared_memory_usage =
101+
static_cast<double>(task_memory_info.resident_size) / 1024.0 / 1024.0 - dirty_memory_usage;
102+
flutter::MemoryUsageInfo memory_usage_info = {
103+
.dirty_memory_usage = dirty_memory_usage,
104+
.owned_shared_memory_usage = owned_shared_memory_usage};
105+
return memory_usage_info;
106+
}
107+
79108
} // namespace flutter

shell/profiling/sampling_profiler.cc

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,28 @@ void SamplingProfiler::Start() const {
3232
void SamplingProfiler::SampleRepeatedly(fml::TimeDelta task_delay) const {
3333
profiler_task_runner_->PostDelayedTask(
3434
[profiler = this, task_delay = task_delay, sampler = sampler_]() {
35+
// TODO(kaushikiska): consider buffering these every n seconds to
36+
// avoid spamming the trace buffer.
3537
const ProfileSample usage = sampler();
3638
if (usage.cpu_usage) {
3739
const auto& cpu_usage = usage.cpu_usage;
38-
// TODO(kaushikiska): consider buffering these every n seconds to
39-
// avoid spamming the trace buffer.
4040
std::string total_cpu_usage =
4141
std::to_string(cpu_usage->total_cpu_usage);
4242
std::string num_threads = std::to_string(cpu_usage->num_threads);
4343
TRACE_EVENT_INSTANT2("flutter::profiling", "CpuUsage",
4444
"total_cpu_usage", total_cpu_usage.c_str(),
4545
"num_threads", num_threads.c_str());
4646
}
47+
if (usage.memory_usage) {
48+
std::string dirty_memory_usage =
49+
std::to_string(usage.memory_usage->dirty_memory_usage);
50+
std::string owned_shared_memory_usage =
51+
std::to_string(usage.memory_usage->owned_shared_memory_usage);
52+
TRACE_EVENT_INSTANT2("flutter::profiling", "MemoryUsage",
53+
"dirty_memory_usage", dirty_memory_usage.c_str(),
54+
"owned_shared_memory_usage",
55+
owned_shared_memory_usage.c_str());
56+
}
4757
profiler->SampleRepeatedly(task_delay);
4858
},
4959
task_delay);

shell/profiling/sampling_profiler.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,29 @@ struct CpuUsageInfo {
2929
double total_cpu_usage;
3030
};
3131

32+
/**
33+
* @brief Memory usage stats. `dirty_memory_usage` is the the memory usage (in
34+
* MB) such that the app uses its physical memory for dirty memory. Dirty memory
35+
* is the memory data that cannot be paged to disk. `owned_shared_memory_usage`
36+
* is the memory usage (in MB) such that the app uses its physicaal memory for
37+
* shared memory, including loaded frameworks and executables. On iOS, it's
38+
* `physical memory - dirty memory`.
39+
*/
40+
struct MemoryUsageInfo {
41+
double dirty_memory_usage;
42+
double owned_shared_memory_usage;
43+
};
44+
3245
/**
3346
* @brief Container for the metrics we collect during each run of `Sampler`.
34-
* This currently holds `CpuUsageInfo` but the intent is to expand it to other
35-
* metrics.
47+
* This currently holds `CpuUsageInfo` and `MemoryUsageInfo` but the intent
48+
* is to expand it to other metrics.
3649
*
3750
* @see flutter::Sampler
3851
*/
3952
struct ProfileSample {
4053
std::optional<CpuUsageInfo> cpu_usage;
54+
std::optional<MemoryUsageInfo> memory_usage;
4155
};
4256

4357
/**

0 commit comments

Comments
 (0)