| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "android_webview/browser/metrics/memory_metrics_logger.h" |
| |
| #include "base/compiler_specific.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/thread_pool.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "services/resource_coordinator/public/cpp/memory_instrumentation/browser_metrics.h" |
| #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h" |
| |
| using memory_instrumentation::GetPrivateFootprintHistogramName; |
| using memory_instrumentation::HistogramProcessType; |
| |
| namespace metrics { |
| namespace { |
| |
| MemoryMetricsLogger* g_instance = nullptr; |
| |
| // Called once the metrics have been determined. Does the actual logging. |
| void RecordMemoryMetricsImpl( |
| MemoryMetricsLogger::RecordCallback done_callback, |
| bool success, |
| std::unique_ptr<memory_instrumentation::GlobalMemoryDump> dump) { |
| if (!success) { |
| if (done_callback) { |
| std::move(done_callback).Run(false); |
| } |
| return; |
| } |
| |
| uint64_t total_private_footprint_kb = 0; |
| for (const auto& process_dump : dump->process_dumps()) { |
| total_private_footprint_kb += process_dump.os_dump().private_footprint_kb; |
| switch (process_dump.process_type()) { |
| case memory_instrumentation::mojom::ProcessType::BROWSER: { |
| MEMORY_METRICS_HISTOGRAM_MB( |
| GetPrivateFootprintHistogramName(HistogramProcessType::kBrowser), |
| process_dump.os_dump().private_footprint_kb / 1024); |
| break; |
| } |
| case memory_instrumentation::mojom::ProcessType::RENDERER: { |
| // On the desktop this may be attributed to an 'extension', but as |
| // android doesn't support extensions there is no checking. |
| MEMORY_METRICS_HISTOGRAM_MB( |
| GetPrivateFootprintHistogramName(HistogramProcessType::kRenderer), |
| process_dump.os_dump().private_footprint_kb / 1024); |
| break; |
| } |
| case memory_instrumentation::mojom::ProcessType::GPU: { |
| MEMORY_METRICS_HISTOGRAM_MB( |
| GetPrivateFootprintHistogramName(HistogramProcessType::kGpu), |
| process_dump.os_dump().private_footprint_kb / 1024); |
| break; |
| } |
| |
| // Currently this class only records metrics for the browser and |
| // renderer process, as it originated from WebView, where there are no |
| // other processes. |
| case memory_instrumentation::mojom::ProcessType::ARC: |
| [[fallthrough]]; |
| case memory_instrumentation::mojom::ProcessType::UTILITY: |
| [[fallthrough]]; |
| case memory_instrumentation::mojom::ProcessType::PLUGIN: |
| [[fallthrough]]; |
| case memory_instrumentation::mojom::ProcessType::OTHER: |
| break; |
| } |
| } |
| if (total_private_footprint_kb) { |
| MEMORY_METRICS_HISTOGRAM_MB("Memory.Total.PrivateMemoryFootprint", |
| total_private_footprint_kb / 1024); |
| } |
| if (done_callback) { |
| std::move(done_callback).Run(true); |
| } |
| } |
| |
| } // namespace |
| |
| // State is used to trigger logging to stop. State is accessed on both the main |
| // thread and the background task runner. |
| struct MemoryMetricsLogger::State : public base::RefCountedThreadSafe<State> { |
| State() = default; |
| |
| State(const State&) = delete; |
| State& operator=(const State&) = delete; |
| |
| // MemoryInstrumentation requires a SequencedTaskRunner. |
| scoped_refptr<base::SequencedTaskRunner> task_runner; |
| |
| bool stop_logging = false; |
| |
| private: |
| friend class base::RefCountedThreadSafe<State>; |
| |
| ~State() = default; |
| }; |
| |
| MemoryMetricsLogger::MemoryMetricsLogger() |
| : state_(base::MakeRefCounted<State>()) { |
| g_instance = this; |
| state_->task_runner = base::ThreadPool::CreateSequencedTaskRunner({}); |
| state_->task_runner->PostTask( |
| FROM_HERE, |
| base::BindOnce(&MemoryMetricsLogger::RecordMemoryMetricsAfterDelay, |
| state_)); |
| } |
| |
| MemoryMetricsLogger::~MemoryMetricsLogger() { |
| g_instance = nullptr; |
| state_->stop_logging = true; |
| } |
| |
| // static |
| MemoryMetricsLogger* MemoryMetricsLogger::GetInstanceForTesting() { |
| return g_instance; |
| } |
| |
| void MemoryMetricsLogger::ScheduleRecordForTesting( |
| RecordCallback done_callback) { |
| state_->task_runner->PostTask( |
| FROM_HERE, base::BindOnce(&MemoryMetricsLogger::RecordMemoryMetrics, |
| state_, std::move(done_callback))); |
| } |
| |
| // static |
| void MemoryMetricsLogger::RecordMemoryMetricsAfterDelay( |
| scoped_refptr<State> state) { |
| if (state->stop_logging) { |
| return; |
| } |
| |
| state->task_runner->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&MemoryMetricsLogger::RecordMemoryMetrics, state, |
| RecordCallback()), |
| memory_instrumentation::GetDelayForNextMemoryLog()); |
| } |
| |
| // static |
| void MemoryMetricsLogger::RecordMemoryMetrics(scoped_refptr<State> state, |
| RecordCallback done_callback) { |
| auto* instrumentation = |
| memory_instrumentation::MemoryInstrumentation::GetInstance(); |
| if (!instrumentation) { |
| // Content layer is not initialized yet, nothing to log. |
| return; |
| } |
| instrumentation->RequestGlobalDump( |
| {}, base::BindOnce(&RecordMemoryMetricsImpl, std::move(done_callback))); |
| RecordMemoryMetricsAfterDelay(state); |
| } |
| |
| } // namespace metrics |