mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[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:
parent
37d96fe9d1
commit
2187523093
@ -30,6 +30,8 @@ class ProfilerMetricsIOS {
|
||||
private:
|
||||
std::optional<CpuUsageInfo> CpuUsage();
|
||||
|
||||
std::optional<MemoryUsageInfo> MemoryUsage();
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(ProfilerMetricsIOS);
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user