| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "services/tracing/public/cpp/system_metrics_sampler.h" |
| |
| #include "base/check.h" |
| #include "base/no_destructor.h" |
| #include "base/power_monitor/cpu_frequency_utils.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/thread_pool.h" |
| #include "base/time/time.h" |
| #include "base/trace_event/common/trace_event_common.h" |
| #include "base/trace_event/trace_event.h" |
| #include "services/tracing/public/mojom/perfetto_service.mojom.h" |
| #include "third_party/perfetto/include/perfetto/tracing/core/data_source_descriptor.h" |
| #include "third_party/perfetto/protos/perfetto/config/chrome/system_metrics.gen.h" |
| #include "third_party/perfetto/protos/perfetto/config/data_source_config.gen.h" |
| |
| #if BUILDFLAG(IS_WIN) |
| #include <windows.h> |
| |
| // Psapi.h must come after Windows.h. |
| #include <psapi.h> |
| #endif // BUILDFLAG(IS_WIN) |
| |
| namespace tracing { |
| |
| namespace { |
| |
| constexpr base::TimeDelta kDefaultSamplingInterval = base::Milliseconds(500); |
| |
| } // namespace |
| |
| void SystemMetricsSampler::Register(bool system_wide) { |
| perfetto::DataSourceDescriptor desc; |
| desc.set_name(tracing::mojom::kSystemMetricsSourceName); |
| perfetto::DataSource<SystemMetricsSampler>::Register(desc, system_wide); |
| } |
| |
| SystemMetricsSampler::SystemMetricsSampler(bool system_wide) |
| : system_wide_(system_wide), sampling_interval_(kDefaultSamplingInterval) {} |
| SystemMetricsSampler::~SystemMetricsSampler() = default; |
| |
| void SystemMetricsSampler::OnSetup(const SetupArgs& args) { |
| if (args.config->chromium_system_metrics_raw().empty()) { |
| return; |
| } |
| perfetto::protos::gen::ChromiumSystemMetricsConfig config; |
| if (!config.ParseFromString(args.config->chromium_system_metrics_raw())) { |
| DLOG(ERROR) << "Failed to parse chromium_system_metrics"; |
| return; |
| } |
| if (config.has_sampling_interval_ms()) { |
| sampling_interval_ = base::Milliseconds(config.sampling_interval_ms()); |
| } |
| } |
| |
| void SystemMetricsSampler::OnStart(const StartArgs&) { |
| if (system_wide_) { |
| system_sampler_ = base::SequenceBound<SystemSampler>( |
| base::ThreadPool::CreateSequencedTaskRunner({}), sampling_interval_); |
| } |
| process_sampler_ = base::SequenceBound<ProcessSampler>( |
| base::ThreadPool::CreateSequencedTaskRunner({}), sampling_interval_); |
| } |
| |
| void SystemMetricsSampler::OnStop(const StopArgs&) { |
| system_sampler_.Reset(); |
| process_sampler_.Reset(); |
| } |
| |
| SystemMetricsSampler::SystemSampler::SystemSampler( |
| base::TimeDelta sampling_interval) |
| : cpu_probe_{system_cpu::CpuProbe::Create()} { |
| if (cpu_probe_) { |
| cpu_probe_->StartSampling(); |
| } |
| sample_timer_.Start(FROM_HERE, sampling_interval, this, |
| &SystemSampler::SampleSystemMetrics); |
| } |
| |
| SystemMetricsSampler::SystemSampler::~SystemSampler() = default; |
| |
| void SystemMetricsSampler::SystemSampler::SampleSystemMetrics() { |
| if (cpu_probe_) { |
| cpu_probe_->RequestSample(base::BindOnce(&SystemSampler::OnCpuProbeResult, |
| base::Unretained(this))); |
| } |
| std::optional<base::CpuThroughputEstimationResult> cpu_throughput = |
| base::EstimateCpuThroughput(); |
| if (cpu_throughput) { |
| TRACE_COUNTER(TRACE_DISABLED_BY_DEFAULT("system_metrics"), |
| perfetto::CounterTrack("EstimatedCpuThroughput", |
| perfetto::Track::Global(0)), |
| cpu_throughput->estimated_frequency); |
| } |
| |
| #if BUILDFLAG(IS_ANDROID) |
| auto frequencies = cpu_frequency_monitor_.GetCoreFrequencies(); |
| for (const auto& freq : frequencies) { |
| TRACE_COUNTER(TRACE_DISABLED_BY_DEFAULT("system_metrics"), |
| perfetto::CounterTrack("CpuFrequency", freq.core_id.value(), |
| perfetto::Track::Global(0)), |
| freq.freq); |
| } |
| #endif |
| |
| #if BUILDFLAG(IS_WIN) |
| base::CpuFrequencyInfo cpu_info = base::GetCpuFrequencyInfo(); |
| TRACE_COUNTER( |
| TRACE_DISABLED_BY_DEFAULT("system_metrics"), |
| perfetto::CounterTrack("NumActiveCpus", perfetto::Track::Global(0)), |
| cpu_info.num_active_cpus); |
| |
| SampleMemoryMetrics(); |
| #endif |
| } |
| |
| #if BUILDFLAG(IS_WIN) |
| void SystemMetricsSampler::SystemSampler::SampleMemoryMetrics() { |
| MEMORYSTATUSEX mem_status = {}; |
| mem_status.dwLength = sizeof(mem_status); |
| if (!::GlobalMemoryStatusEx(&mem_status)) { |
| return; |
| } |
| |
| TRACE_COUNTER( |
| TRACE_DISABLED_BY_DEFAULT("system_metrics"), |
| perfetto::CounterTrack("CommitMemoryLimit", perfetto::Track::Global(0)), |
| mem_status.ullTotalPageFile); |
| |
| TRACE_COUNTER(TRACE_DISABLED_BY_DEFAULT("system_metrics"), |
| perfetto::CounterTrack("CommitMemoryAvailable", |
| perfetto::Track::Global(0)), |
| mem_status.ullAvailPageFile); |
| TRACE_COUNTER(TRACE_DISABLED_BY_DEFAULT("system_metrics"), |
| perfetto::CounterTrack("AvailablePhysicalMemory", |
| perfetto::Track::Global(0)), |
| mem_status.ullAvailPhys); |
| } |
| #endif // BUILDFLAG(IS_WIN) |
| |
| void SystemMetricsSampler::SystemSampler::OnCpuProbeResult( |
| std::optional<system_cpu::CpuSample> cpu_sample) { |
| if (!cpu_sample) { |
| return; |
| } |
| TRACE_COUNTER( |
| TRACE_DISABLED_BY_DEFAULT("system_metrics"), |
| perfetto::CounterTrack("SystemCpuUsage", perfetto::Track::Global(0)), |
| cpu_sample->cpu_utilization); |
| } |
| |
| SystemMetricsSampler::ProcessSampler::ProcessSampler( |
| base::TimeDelta sampling_interval) { |
| process_metrics_ = base::ProcessMetrics::CreateCurrentProcessMetrics(); |
| SampleProcessMetrics(); |
| sample_timer_.Start(FROM_HERE, sampling_interval, this, |
| &ProcessSampler::SampleProcessMetrics); |
| } |
| |
| SystemMetricsSampler::ProcessSampler::~ProcessSampler() = default; |
| |
| void SystemMetricsSampler::ProcessSampler::SampleProcessMetrics() { |
| auto cpu_usage = process_metrics_->GetPlatformIndependentCPUUsage(); |
| if (cpu_usage.has_value()) { |
| TRACE_COUNTER(TRACE_DISABLED_BY_DEFAULT("system_metrics"), "CpuUsage", |
| *cpu_usage); |
| } |
| auto memory_info = process_metrics_->GetMemoryInfo(); |
| if (memory_info.has_value()) { |
| TRACE_COUNTER(TRACE_DISABLED_BY_DEFAULT("system_metrics"), "ResidentSet", |
| memory_info->resident_set_bytes); |
| #if BUILDFLAG(IS_MAC) |
| TRACE_COUNTER(TRACE_DISABLED_BY_DEFAULT("system_metrics"), |
| "PhysicalMemoryFootprint", |
| memory_info->physical_footprint_bytes); |
| #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \ |
| BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) |
| TRACE_COUNTER(TRACE_DISABLED_BY_DEFAULT("system_metrics"), "VmSwapMemory", |
| memory_info->vm_swap_bytes); |
| TRACE_COUNTER(TRACE_DISABLED_BY_DEFAULT("system_metrics"), "RssAnonMemory", |
| memory_info->rss_anon_bytes); |
| #elif BUILDFLAG(IS_WIN) |
| TRACE_COUNTER(TRACE_DISABLED_BY_DEFAULT("system_metrics"), "PrivateMemory", |
| memory_info->private_bytes); |
| #endif // BUILDFLAG(IS_WIN) |
| } |
| } |
| |
| } // namespace tracing |