// Copyright 2015 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 "content/browser/tracing/background_tracing_rule.h" #include #include "base/bind.h" #include "base/metrics/histogram_macros.h" #include "base/metrics/statistics_recorder.h" #include "base/rand_util.h" #include "base/strings/safe_sprintf.h" #include "base/values.h" #include "components/tracing/tracing_messages.h" #include "content/browser/tracing/background_tracing_manager_impl.h" #include "content/browser/tracing/trace_message_filter.h" #include "content/public/browser/browser_thread.h" namespace { const char kConfigRuleKey[] = "rule"; const char kConfigCategoryKey[] = "category"; const char kConfigRuleTriggerNameKey[] = "trigger_name"; const char kConfigRuleHistogramNameKey[] = "histogram_name"; const char kConfigRuleHistogramValueKey[] = "histogram_value"; const char kConfigRuleRandomIntervalTimeoutMin[] = "timeout_min"; const char kConfigRuleRandomIntervalTimeoutMax[] = "timeout_max"; const char kPreemptiveConfigRuleMonitorNamed[] = "MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED"; const char kPreemptiveConfigRuleMonitorHistogram[] = "MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE"; const char kReactiveConfigRuleTraceOnNavigationUntilTriggerOrFull[] = "TRACE_ON_NAVIGATION_UNTIL_TRIGGER_OR_FULL"; const char kReactiveConfigRuleTraceAtRandomIntervals[] = "TRACE_AT_RANDOM_INTERVALS"; const char kTraceAtRandomIntervalsEventName[] = "ReactiveTraceAtRandomIntervals"; const int kReactiveConfigNavigationTimeout = 30; const int kReactiveTraceRandomStartTimeMin = 60; const int kReactiveTraceRandomStartTimeMax = 120; } // namespace namespace content { BackgroundTracingRule::BackgroundTracingRule() {} BackgroundTracingRule::~BackgroundTracingRule() {} bool BackgroundTracingRule::ShouldTriggerNamedEvent( const std::string& named_event) const { return false; } BackgroundTracingConfigImpl::CategoryPreset BackgroundTracingRule::GetCategoryPreset() const { return BackgroundTracingConfigImpl::BENCHMARK; } namespace { class NamedTriggerRule : public BackgroundTracingRule { public: NamedTriggerRule(const std::string& named_event) : named_event_(named_event) {} void IntoDict(base::DictionaryValue* dict) const override { DCHECK(dict); dict->SetString(kConfigRuleKey, kPreemptiveConfigRuleMonitorNamed); dict->SetString(kConfigRuleTriggerNameKey, named_event_.c_str()); } bool ShouldTriggerNamedEvent(const std::string& named_event) const override { return named_event == named_event_; } private: std::string named_event_; }; class HistogramRule : public BackgroundTracingRule, public TracingControllerImpl::TraceMessageFilterObserver { public: HistogramRule(const std::string& histogram_name, int histogram_value) : histogram_name_(histogram_name), histogram_value_(histogram_value) {} ~HistogramRule() override { base::StatisticsRecorder::ClearCallback(histogram_name_); TracingControllerImpl::GetInstance()->RemoveTraceMessageFilterObserver( this); } // BackgroundTracingRule implementation void Install() override { base::StatisticsRecorder::SetCallback( histogram_name_, base::Bind(&HistogramRule::OnHistogramChangedCallback, base::Unretained(this), histogram_name_, histogram_value_)); TracingControllerImpl::GetInstance()->AddTraceMessageFilterObserver(this); } void IntoDict(base::DictionaryValue* dict) const override { DCHECK(dict); dict->SetString(kConfigRuleKey, kPreemptiveConfigRuleMonitorHistogram); dict->SetString(kConfigRuleHistogramNameKey, histogram_name_.c_str()); dict->SetInteger(kConfigRuleHistogramValueKey, histogram_value_); } void OnHistogramTrigger(const std::string& histogram_name) const override { if (histogram_name != histogram_name_) return; content::BrowserThread::PostTask( content::BrowserThread::UI, FROM_HERE, base::Bind( &BackgroundTracingManagerImpl::TriggerPreemptiveFinalization, base::Unretained(BackgroundTracingManagerImpl::GetInstance()))); } // TracingControllerImpl::TraceMessageFilterObserver implementation void OnTraceMessageFilterAdded(TraceMessageFilter* filter) override { filter->Send( new TracingMsg_SetUMACallback(histogram_name_, histogram_value_)); } void OnTraceMessageFilterRemoved(TraceMessageFilter* filter) override { filter->Send(new TracingMsg_ClearUMACallback(histogram_name_)); } void OnHistogramChangedCallback(const std::string& histogram_name, base::Histogram::Sample reference_value, base::Histogram::Sample actual_value) { if (reference_value > actual_value) return; OnHistogramTrigger(histogram_name); } private: std::string histogram_name_; int histogram_value_; }; class ReactiveTraceForNSOrTriggerOrFullRule : public BackgroundTracingRule { public: ReactiveTraceForNSOrTriggerOrFullRule( const std::string& named_event, BackgroundTracingConfigImpl::CategoryPreset category_preset) : named_event_(named_event), category_preset_(category_preset) {} // BackgroundTracingRule implementation void IntoDict(base::DictionaryValue* dict) const override { DCHECK(dict); dict->SetString( kConfigCategoryKey, BackgroundTracingConfigImpl::CategoryPresetToString(category_preset_)); dict->SetString(kConfigRuleKey, kReactiveConfigRuleTraceOnNavigationUntilTriggerOrFull); dict->SetString(kConfigRuleTriggerNameKey, named_event_.c_str()); } bool ShouldTriggerNamedEvent(const std::string& named_event) const override { return named_event == named_event_; } int GetReactiveTimeout() const override { return kReactiveConfigNavigationTimeout; } BackgroundTracingConfigImpl::CategoryPreset GetCategoryPreset() const override { return category_preset_; } private: std::string named_event_; BackgroundTracingConfigImpl::CategoryPreset category_preset_; }; class ReactiveTraceAtRandomIntervalsRule : public BackgroundTracingRule { public: ReactiveTraceAtRandomIntervalsRule( BackgroundTracingConfigImpl::CategoryPreset category_preset, int timeout_min, int timeout_max) : category_preset_(category_preset), timeout_min_(timeout_min), timeout_max_(timeout_max) { named_event_ = GenerateUniqueName(); } ~ReactiveTraceAtRandomIntervalsRule() override {} void IntoDict(base::DictionaryValue* dict) const override { DCHECK(dict); dict->SetString( kConfigCategoryKey, BackgroundTracingConfigImpl::CategoryPresetToString(category_preset_)); dict->SetString(kConfigRuleKey, kReactiveConfigRuleTraceAtRandomIntervals); dict->SetInteger(kConfigRuleRandomIntervalTimeoutMin, timeout_min_); dict->SetInteger(kConfigRuleRandomIntervalTimeoutMax, timeout_max_); } void Install() override { handle_ = BackgroundTracingManagerImpl::GetInstance()->RegisterTriggerType( named_event_.c_str()); StartTimer(); } void OnStartedFinalizing(bool success) { if (!success) return; StartTimer(); } void OnTriggerTimer() { BackgroundTracingManagerImpl::GetInstance()->TriggerNamedEvent( handle_, base::Bind(&ReactiveTraceAtRandomIntervalsRule::OnStartedFinalizing, base::Unretained(this))); } void StartTimer() { int time_to_wait = base::RandInt(kReactiveTraceRandomStartTimeMin, kReactiveTraceRandomStartTimeMax); trigger_timer_.Start( FROM_HERE, base::TimeDelta::FromSeconds(time_to_wait), base::Bind(&ReactiveTraceAtRandomIntervalsRule::OnTriggerTimer, base::Unretained(this))); } int GetReactiveTimeout() const override { return base::RandInt(timeout_min_, timeout_max_); } bool ShouldTriggerNamedEvent(const std::string& named_event) const override { return named_event == named_event_; } BackgroundTracingConfigImpl::CategoryPreset GetCategoryPreset() const override { return category_preset_; } std::string GenerateUniqueName() const { static int ids = 0; char work_buffer[256]; base::strings::SafeSNPrintf(work_buffer, sizeof(work_buffer), "%s_%d", kTraceAtRandomIntervalsEventName, ids++); return work_buffer; } private: std::string named_event_; base::OneShotTimer trigger_timer_; BackgroundTracingConfigImpl::CategoryPreset category_preset_; BackgroundTracingManagerImpl::TriggerHandle handle_; int timeout_min_; int timeout_max_; }; } // namespace int BackgroundTracingRule::GetReactiveTimeout() const { return -1; } scoped_ptr BackgroundTracingRule::PreemptiveRuleFromDict( const base::DictionaryValue* dict) { DCHECK(dict); std::string type; if (!dict->GetString(kConfigRuleKey, &type)) return nullptr; if (type == kPreemptiveConfigRuleMonitorNamed) { std::string trigger_name; if (!dict->GetString(kConfigRuleTriggerNameKey, &trigger_name)) return nullptr; return scoped_ptr( new NamedTriggerRule(trigger_name)); } if (type == kPreemptiveConfigRuleMonitorHistogram) { std::string histogram_name; if (!dict->GetString(kConfigRuleHistogramNameKey, &histogram_name)) return nullptr; int histogram_value; if (!dict->GetInteger(kConfigRuleHistogramValueKey, &histogram_value)) return nullptr; return scoped_ptr( new HistogramRule(histogram_name, histogram_value)); } return nullptr; } scoped_ptr BackgroundTracingRule::ReactiveRuleFromDict( const base::DictionaryValue* dict, BackgroundTracingConfigImpl::CategoryPreset category_preset) { DCHECK(dict); std::string type; if (!dict->GetString(kConfigRuleKey, &type)) return nullptr; if (type == kReactiveConfigRuleTraceOnNavigationUntilTriggerOrFull) { std::string trigger_name; if (!dict->GetString(kConfigRuleTriggerNameKey, &trigger_name)) return nullptr; return scoped_ptr( new ReactiveTraceForNSOrTriggerOrFullRule(trigger_name, category_preset)); } if (type == kReactiveConfigRuleTraceAtRandomIntervals) { int timeout_min; if (!dict->GetInteger(kConfigRuleRandomIntervalTimeoutMin, &timeout_min)) return nullptr; int timeout_max; if (!dict->GetInteger(kConfigRuleRandomIntervalTimeoutMax, &timeout_max)) return nullptr; if (timeout_min > timeout_max) return nullptr; return scoped_ptr( new ReactiveTraceAtRandomIntervalsRule(category_preset, timeout_min, timeout_max)); } return nullptr; } } // namespace content