blob: c88f9fbf439ade150051b7f5f2ed6a88b1eac9d7 [file] [log] [blame]
// Copyright 2017 The Chromium 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 "chrome/browser/profiling_host/background_profiling_triggers.h"
#include "base/task_scheduler/post_task.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiling_host/profiling_process_host.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/process_type.h"
#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
namespace profiling {
namespace {
// Check memory usage every hour. Trigger slow report if needed.
const int kRepeatingCheckMemoryDelayInHours = 1;
const int kThrottledReportRepeatingCheckMemoryDelayInHours = 12;
#if defined(OS_ANDROID)
const size_t kBrowserProcessMallocTriggerKb = 100 * 1024; // 100 MB
const size_t kGPUProcessMallocTriggerKb = 40 * 1024; // 40 MB
const size_t kRendererProcessMallocTriggerKb = 125 * 1024; // 125 MB
#else
const size_t kBrowserProcessMallocTriggerKb = 400 * 1024; // 400 MB
const size_t kGPUProcessMallocTriggerKb = 400 * 1024; // 400 MB
const size_t kRendererProcessMallocTriggerKb = 500 * 1024; // 500 MB
#endif // OS_ANDROID
int GetContentProcessType(
const memory_instrumentation::mojom::ProcessType& type) {
using memory_instrumentation::mojom::ProcessType;
switch (type) {
case ProcessType::BROWSER:
return content::ProcessType::PROCESS_TYPE_BROWSER;
case ProcessType::RENDERER:
return content::ProcessType::PROCESS_TYPE_RENDERER;
case ProcessType::GPU:
return content::ProcessType::PROCESS_TYPE_GPU;
case ProcessType::UTILITY:
return content::ProcessType::PROCESS_TYPE_UTILITY;
case ProcessType::PLUGIN:
return content::ProcessType::PROCESS_TYPE_PLUGIN_DEPRECATED;
case ProcessType::OTHER:
return content::ProcessType::PROCESS_TYPE_UNKNOWN;
}
NOTREACHED();
return content::ProcessType::PROCESS_TYPE_UNKNOWN;
}
} // namespace
BackgroundProfilingTriggers::BackgroundProfilingTriggers(
ProfilingProcessHost* host)
: host_(host), weak_ptr_factory_(this) {
DCHECK(host_);
}
BackgroundProfilingTriggers::~BackgroundProfilingTriggers() {}
void BackgroundProfilingTriggers::StartTimer() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
// Register a repeating timer to check memory usage periodically.
timer_.Start(FROM_HERE,
base::TimeDelta::FromHours(kRepeatingCheckMemoryDelayInHours),
base::BindRepeating(
&BackgroundProfilingTriggers::PerformMemoryUsageChecks,
weak_ptr_factory_.GetWeakPtr()));
}
bool BackgroundProfilingTriggers::IsAllowedToUpload() const {
if (!ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled()) {
return false;
}
// Do not upload if there is an incognito session running in any profile.
std::vector<Profile*> profiles =
g_browser_process->profile_manager()->GetLoadedProfiles();
for (Profile* profile : profiles) {
if (profile->HasOffTheRecordProfile()) {
return false;
}
}
return true;
}
bool BackgroundProfilingTriggers::IsOverTriggerThreshold(
int content_process_type,
uint32_t private_footprint_kb) {
switch (content_process_type) {
case content::ProcessType::PROCESS_TYPE_BROWSER:
return private_footprint_kb > kBrowserProcessMallocTriggerKb;
case content::ProcessType::PROCESS_TYPE_GPU:
return private_footprint_kb > kGPUProcessMallocTriggerKb;
case content::ProcessType::PROCESS_TYPE_RENDERER:
return private_footprint_kb > kRendererProcessMallocTriggerKb;
default:
return false;
}
}
void BackgroundProfilingTriggers::PerformMemoryUsageChecks() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
if (!IsAllowedToUpload()) {
return;
}
auto callback = base::BindOnce(
[](base::WeakPtr<BackgroundProfilingTriggers> weak_ptr,
std::vector<base::ProcessId> result) {
memory_instrumentation::MemoryInstrumentation::GetInstance()
->RequestGlobalDump(
base::Bind(&BackgroundProfilingTriggers::OnReceivedMemoryDump,
std::move(weak_ptr), std::move(result)));
},
weak_ptr_factory_.GetWeakPtr());
host_->GetProfiledPids(std::move(callback));
}
void BackgroundProfilingTriggers::OnReceivedMemoryDump(
std::vector<base::ProcessId> profiled_pids,
bool success,
std::unique_ptr<memory_instrumentation::GlobalMemoryDump> dump) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
if (!success) {
return;
}
bool should_send_report = false;
for (const auto& proc : dump->process_dumps()) {
if (std::find(profiled_pids.begin(), profiled_pids.end(), proc.pid()) ==
profiled_pids.end())
continue;
if (IsOverTriggerThreshold(GetContentProcessType(proc.process_type()),
proc.os_dump().private_footprint_kb)) {
should_send_report = true;
break;
}
}
if (should_send_report) {
TriggerMemoryReport();
// If a report was sent, throttle the memory data collection rate to
// kThrottledReportRepeatingCheckMemoryDelayInHours to avoid sending too
// many reports from a known problematic client.
timer_.Start(FROM_HERE,
base::TimeDelta::FromHours(
kThrottledReportRepeatingCheckMemoryDelayInHours),
base::BindRepeating(
&BackgroundProfilingTriggers::PerformMemoryUsageChecks,
weak_ptr_factory_.GetWeakPtr()));
}
}
void BackgroundProfilingTriggers::TriggerMemoryReport() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
host_->RequestProcessReport("MEMLOG_BACKGROUND_TRIGGER");
}
} // namespace profiling