blob: 9f0350627cf93b8949705e058285e969a3c32096 [file] [log] [blame]
// Copyright 2016 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/metrics/leak_detector/leak_detector_controller.h"
#include <algorithm>
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/sys_info.h"
#include "components/metrics/leak_detector/gnu_build_id_reader.h"
#include "components/variations/variations_associated_data.h"
#include "content/public/browser/browser_thread.h"
namespace metrics {
namespace {
using ParamsMap = std::map<std::string, std::string>;
// Returns a mapping of param names to param values, obtained from the
// variations system. Both names and values are strings.
ParamsMap GetRawVariationParams() {
const char kFieldTrialName[] = "RuntimeMemoryLeakDetector";
ParamsMap result;
variations::GetVariationParams(kFieldTrialName, &result);
return result;
}
// Reads the raw variation parameters and parses them to obtain values for the
// parameters used by the memory leak detector itself. Any parameters not
// present in the variation info or that cannot be parsed will be filled in with
// default values. Returns a MemoryLeakReportProto with the parameter fields
// filled in.
MemoryLeakReportProto::Params GetLeakDetectorParams() {
// Variation parameter names.
const char kSamplingRateParam[] = "sampling_rate";
const char kMaxStackDepthParam[] = "max_stack_depth";
const char kAnalysisIntervalKbParam[] = "analysis_interval_kb";
const char kSizeSuspicionThresholdParam[] = "size_suspicion_threshold";
const char kCallStackSuspicionThresholdParam[] =
"call_stack_suspicion_threshold";
// Default parameter values.
double kDefaultSamplingRate = 1.0f / 256;
size_t kDefaultMaxStackDepth = 4;
uint64_t kDefaultAnalysisIntervalKb = 32768;
uint32_t kDefaultSizeSuspicionThreshold = 4;
uint32_t kDefaultCallStackSuspicionThreshold = 4;
double sampling_rate = 0;
size_t max_stack_depth = 0;
uint64_t analysis_interval_kb = 0;
uint32_t size_suspicion_threshold = 0;
uint32_t call_stack_suspicion_threshold = 0;
const ParamsMap params = GetRawVariationParams();
// Even if the variation param data does not exist and |params| ends up empty,
// the below code will assign default values.
auto iter = params.find(kSamplingRateParam);
if (iter == params.end() ||
!base::StringToDouble(iter->second, &sampling_rate)) {
sampling_rate = kDefaultSamplingRate;
}
iter = params.find(kMaxStackDepthParam);
if (iter == params.end() ||
!base::StringToSizeT(iter->second, &max_stack_depth)) {
max_stack_depth = kDefaultMaxStackDepth;
}
iter = params.find(kAnalysisIntervalKbParam);
if (iter == params.end() ||
!base::StringToUint64(iter->second, &analysis_interval_kb)) {
analysis_interval_kb = kDefaultAnalysisIntervalKb;
}
iter = params.find(kSizeSuspicionThresholdParam);
if (iter == params.end() ||
!base::StringToUint(iter->second, &size_suspicion_threshold)) {
size_suspicion_threshold = kDefaultSizeSuspicionThreshold;
}
iter = params.find(kCallStackSuspicionThresholdParam);
if (iter == params.end() ||
!base::StringToUint(iter->second, &call_stack_suspicion_threshold)) {
call_stack_suspicion_threshold = kDefaultCallStackSuspicionThreshold;
}
MemoryLeakReportProto_Params result;
result.set_sampling_rate(sampling_rate);
result.set_max_stack_depth(max_stack_depth);
result.set_analysis_interval_bytes(analysis_interval_kb * 1024);
result.set_size_suspicion_threshold(size_suspicion_threshold);
result.set_call_stack_suspicion_threshold(call_stack_suspicion_threshold);
return result;
}
// Parses the parameters related to randomly enabling leak detector on different
// processes, from the raw parameter strings provided by the variations.
// Args:
// - browser_probability: probability that the leak detector will be enabled on
// browser process (spawned once per session).
// - renderer_probability: probability that the leak detector will be enabled on
// renderer process (spawned many times per session).
// - max_renderer_processes_enabled: The maximum number of renderer processes on
// which the leak detector can be enabled
// simultaneously.
//
// Probabilities are in the range [0, 1]. Anything higher or lower will not be
// clamped but it will not affect the outcome, since these probabilities are
// compared against the value of base::RandDouble() (aka the "dice roll"), which
// will be within this range.
void GetLeakDetectorEnableParams(double* browser_probability,
double* renderer_probability,
int* max_renderer_processes_enabled) {
const char kBrowserEnableProbabilityParam[] =
"browser_process_enable_probability";
const char kRendererEnableProbabilityParam[] =
"renderer_process_enable_probability";
const char kMaxRendererProcessesEnabledParam[] =
"max_renderer_processes_enabled";
const double kDefaultProbability = 0.0;
const int kDefaultNumProcessesEnabled = 0;
const ParamsMap params = GetRawVariationParams();
auto iter = params.find(kBrowserEnableProbabilityParam);
if (iter == params.end() ||
!base::StringToDouble(iter->second, browser_probability)) {
*browser_probability = kDefaultProbability;
}
iter = params.find(kRendererEnableProbabilityParam);
if (iter == params.end() ||
!base::StringToDouble(iter->second, renderer_probability)) {
*renderer_probability = kDefaultProbability;
}
iter = params.find(kMaxRendererProcessesEnabledParam);
if (iter == params.end() ||
!base::StringToInt(iter->second, max_renderer_processes_enabled)) {
*max_renderer_processes_enabled = kDefaultNumProcessesEnabled;
}
}
} // namespace
LeakDetectorController::LeakDetectorController()
: params_(GetLeakDetectorParams()),
browser_process_enable_probability_(0),
renderer_process_enable_probability_(0),
max_renderer_processes_with_leak_detector_enabled_(0),
num_renderer_processes_with_leak_detector_enabled_(0),
enable_collect_memory_usage_step_(true),
waiting_for_collect_memory_usage_step_(false),
weak_ptr_factory_(this) {
// Read the build ID once and store it.
leak_detector::gnu_build_id_reader::ReadBuildID(&build_id_);
GetLeakDetectorEnableParams(
&browser_process_enable_probability_,
&renderer_process_enable_probability_,
&max_renderer_processes_with_leak_detector_enabled_);
// Register the LeakDetectorController with the remote controller, so this
// class can send/receive data to/from remote processes.
LeakDetectorRemoteController::SetLocalControllerInstance(this);
// Conditionally launch browser process based on probability.
if (base::RandDouble() < browser_process_enable_probability_) {
LeakDetector* detector = LeakDetector::GetInstance();
detector->AddObserver(this);
// Leak detector parameters are stored in |params_|.
detector->Init(params_, content::BrowserThread::GetTaskRunnerForThread(
content::BrowserThread::UI));
}
}
LeakDetectorController::~LeakDetectorController() {
DCHECK(thread_checker_.CalledOnValidThread());
LeakDetector::GetInstance()->RemoveObserver(this);
LeakDetectorRemoteController::SetLocalControllerInstance(nullptr);
}
void LeakDetectorController::GetLeakReports(
std::vector<MemoryLeakReportProto>* reports) {
DCHECK(thread_checker_.CalledOnValidThread());
reports->swap(stored_reports_);
stored_reports_.clear();
}
void LeakDetectorController::OnLeaksFound(
const std::vector<MemoryLeakReportProto>& reports) {
StoreLeakReports(reports, MemoryLeakReportProto::BROWSER_PROCESS);
}
MemoryLeakReportProto_Params
LeakDetectorController::GetParamsAndRecordRequest() {
if (ShouldRandomlyEnableLeakDetectorOnRendererProcess()) {
++num_renderer_processes_with_leak_detector_enabled_;
return params_;
}
// If the leak detector is not to be enabled on the remote process, send an
// empty MemoryLeakReportProto_Params protobuf. The remote process will not
// initialize the leak detector since |sampling_rate| is 0.
return MemoryLeakReportProto_Params();
}
void LeakDetectorController::SendLeakReports(
const std::vector<MemoryLeakReportProto>& reports) {
StoreLeakReports(reports, MemoryLeakReportProto::RENDERER_PROCESS);
}
void LeakDetectorController::OnRemoteProcessShutdown() {
DCHECK_GT(num_renderer_processes_with_leak_detector_enabled_, 0);
--num_renderer_processes_with_leak_detector_enabled_;
}
LeakDetectorController::TotalMemoryGrowthTracker::TotalMemoryGrowthTracker()
: total_usage_kb_(0) {}
LeakDetectorController::TotalMemoryGrowthTracker::~TotalMemoryGrowthTracker() {}
bool LeakDetectorController::TotalMemoryGrowthTracker::UpdateSample(
base::ProcessId pid,
int sample,
int* diff) {
total_usage_kb_ += sample;
return false;
}
bool LeakDetectorController::ShouldRandomlyEnableLeakDetectorOnRendererProcess()
const {
return base::RandDouble() < renderer_process_enable_probability_ &&
num_renderer_processes_with_leak_detector_enabled_ <
max_renderer_processes_with_leak_detector_enabled_;
}
void LeakDetectorController::OnMemoryDetailCollectionDone(size_t index) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(waiting_for_collect_memory_usage_step_);
waiting_for_collect_memory_usage_step_ = false;
// The available physical memory must be translated from bytes
// and the total memory usage must be translated from kb.
MemoryLeakReportProto::MemoryUsageInfo mem_info;
mem_info.set_available_ram_mb(
base::SysInfo::AmountOfAvailablePhysicalMemory() / 1024 / 1024);
mem_info.set_chrome_ram_usage_mb(
total_memory_growth_tracker_.total_usage_kb() / 1024);
// Update all reports that arrived in the meantime.
for (; index < stored_reports_.size(); index++) {
stored_reports_[index].mutable_memory_usage_info()->CopyFrom(mem_info);
}
}
void LeakDetectorController::StoreLeakReports(
const std::vector<MemoryLeakReportProto>& reports,
MemoryLeakReportProto::ProcessType process_type) {
DCHECK(thread_checker_.CalledOnValidThread());
// Postpone a task of collecting info about the memory usage.
// When the task is done, all reports collected after this point
// will be updated, unless GetLeakReports() gets called first.
if (enable_collect_memory_usage_step_ &&
!waiting_for_collect_memory_usage_step_) {
waiting_for_collect_memory_usage_step_ = true;
total_memory_growth_tracker_.reset();
// Idea of using MetricsMemoryDetails is similar as in
// ChromeMetricsServiceClient. However, here generation of histogram data
// is suppressed.
base::Closure callback =
base::Bind(&LeakDetectorController::OnMemoryDetailCollectionDone,
weak_ptr_factory_.GetWeakPtr(), stored_reports_.size());
scoped_refptr<MetricsMemoryDetails> details(
new MetricsMemoryDetails(callback, &total_memory_growth_tracker_));
details->set_generate_histograms(false);
details->StartFetch();
}
for (const auto& report : reports) {
// Store the report and insert stored parameters.
stored_reports_.push_back(report);
stored_reports_.back().mutable_params()->CopyFrom(params_);
stored_reports_.back().set_source_process(process_type);
stored_reports_.back().mutable_build_id()->assign(build_id_.begin(),
build_id_.end());
}
}
} // namespace metrics