[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.
This commit is contained in:
Albert Wang 2020-05-21 08:55:40 -07:00 committed by GitHub
parent 37d96fe9d1
commit 2187523093
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 5 deletions

View File

@ -30,6 +30,8 @@ class ProfilerMetricsIOS {
private:
std::optional<CpuUsageInfo> CpuUsage();
std::optional<MemoryUsageInfo> MemoryUsage();
FML_DISALLOW_COPY_AND_ASSIGN(ProfilerMetricsIOS);
};

View File

@ -30,7 +30,7 @@ class MachThreads {
namespace flutter {
ProfileSample ProfilerMetricsIOS::GenerateSample() {
return {.cpu_usage = CpuUsage()};
return {.cpu_usage = CpuUsage(), .memory_usage = MemoryUsage()};
}
std::optional<CpuUsageInfo> ProfilerMetricsIOS::CpuUsage() {
@ -76,4 +76,33 @@ std::optional<CpuUsageInfo> ProfilerMetricsIOS::CpuUsage() {
return cpu_usage_info;
}
std::optional<MemoryUsageInfo> 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_info_t>(&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<double>(task_memory_info.phys_footprint) / 1024.0 / 1024.0;
const double owned_shared_memory_usage =
static_cast<double>(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

View File

@ -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);

View File

@ -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<CpuUsageInfo> cpu_usage;
std::optional<MemoryUsageInfo> memory_usage;
};
/**