| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "flutter/shell/profiling/sampling_profiler.h" |
| |
| #include <utility> |
| |
| namespace flutter { |
| |
| SamplingProfiler::SamplingProfiler( |
| const char* thread_label, |
| fml::RefPtr<fml::TaskRunner> profiler_task_runner, |
| Sampler sampler, |
| int num_samples_per_sec) |
| : thread_label_(thread_label), |
| profiler_task_runner_(std::move(profiler_task_runner)), |
| sampler_(std::move(sampler)), |
| num_samples_per_sec_(num_samples_per_sec) {} |
| |
| SamplingProfiler::~SamplingProfiler() { |
| if (is_running_) { |
| Stop(); |
| } |
| } |
| |
| void SamplingProfiler::Start() { |
| if (!profiler_task_runner_) { |
| return; |
| } |
| FML_CHECK(num_samples_per_sec_ > 0) |
| << "number of samples must be a positive integer, got: " |
| << num_samples_per_sec_; |
| double delay_between_samples = 1.0 / num_samples_per_sec_; |
| auto task_delay = fml::TimeDelta::FromSecondsF(delay_between_samples); |
| UpdateObservatoryThreadName(); |
| is_running_ = true; |
| SampleRepeatedly(task_delay); |
| } |
| |
| void SamplingProfiler::Stop() { |
| FML_DCHECK(is_running_); |
| auto latch = std::make_unique<fml::AutoResetWaitableEvent>(); |
| shutdown_latch_.store(latch.get()); |
| latch->Wait(); |
| shutdown_latch_.store(nullptr); |
| is_running_ = false; |
| } |
| |
| void SamplingProfiler::SampleRepeatedly(fml::TimeDelta task_delay) const { |
| profiler_task_runner_->PostDelayedTask( |
| [profiler = this, task_delay = task_delay, sampler = sampler_, |
| &shutdown_latch = shutdown_latch_]() { |
| // 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; |
| std::string total_cpu_usage = |
| std::to_string(cpu_usage->total_cpu_usage); |
| std::string num_threads = std::to_string(cpu_usage->num_threads); |
| TRACE_EVENT_INSTANT2("flutter::profiling", "CpuUsage", |
| "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()); |
| } |
| if (usage.gpu_usage) { |
| std::string gpu_usage = |
| std::to_string(usage.gpu_usage->percent_usage); |
| TRACE_EVENT_INSTANT1("flutter::profiling", "GpuUsage", "gpu_usage", |
| gpu_usage.c_str()); |
| } |
| if (shutdown_latch.load()) { |
| shutdown_latch.load()->Signal(); |
| } else { |
| profiler->SampleRepeatedly(task_delay); |
| } |
| }, |
| task_delay); |
| } |
| |
| void SamplingProfiler::UpdateObservatoryThreadName() const { |
| FML_CHECK(profiler_task_runner_); |
| |
| profiler_task_runner_->PostTask( |
| [label = thread_label_ + std::string{".profiler"}]() { |
| Dart_SetThreadName(label.c_str()); |
| }); |
| } |
| |
| } // namespace flutter |