// 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 kConfigRuleTriggerDelay[] = "trigger_delay"; const char kConfigRuleTriggerChance[] = "trigger_chance"; const char kConfigRuleHistogramNameKey[] = "histogram_name"; const char kConfigRuleHistogramValueOldKey[] = "histogram_value"; const char kConfigRuleHistogramValue1Key[] = "histogram_lower_value"; const char kConfigRuleHistogramValue2Key[] = "histogram_upper_value"; const char kConfigRuleHistogramRepeatKey[] = "histogram_repeat"; 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() : trigger_chance_(1.0) {} BackgroundTracingRule::~BackgroundTracingRule() {} bool BackgroundTracingRule::ShouldTriggerNamedEvent( const std::string& named_event) const { return false; } BackgroundTracingConfigImpl::CategoryPreset BackgroundTracingRule::GetCategoryPreset() const { return BackgroundTracingConfigImpl::BENCHMARK; } int BackgroundTracingRule::GetTraceTimeout() const { return -1; } void BackgroundTracingRule::IntoDict(base::DictionaryValue* dict) const { DCHECK(dict); if (trigger_chance_ < 1.0) dict->SetDouble(kConfigRuleTriggerChance, trigger_chance_); } void BackgroundTracingRule::Setup(const base::DictionaryValue* dict) { dict->GetDouble(kConfigRuleTriggerChance, &trigger_chance_); } namespace { class NamedTriggerRule : public BackgroundTracingRule { private: NamedTriggerRule(const std::string& named_event) : named_event_(named_event) {} public: static scoped_ptr Create( const base::DictionaryValue* dict) { std::string trigger_name; if (!dict->GetString(kConfigRuleTriggerNameKey, &trigger_name)) return nullptr; return scoped_ptr( new NamedTriggerRule(trigger_name)); } void IntoDict(base::DictionaryValue* dict) const override { DCHECK(dict); BackgroundTracingRule::IntoDict(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 { private: HistogramRule(const std::string& histogram_name, int histogram_lower_value, int histogram_upper_value, bool repeat, int trigger_delay) : histogram_name_(histogram_name), histogram_lower_value_(histogram_lower_value), histogram_upper_value_(histogram_upper_value), repeat_(repeat), trigger_delay_(trigger_delay) {} public: static scoped_ptr Create( const base::DictionaryValue* dict) { std::string histogram_name; if (!dict->GetString(kConfigRuleHistogramNameKey, &histogram_name)) return nullptr; // Optional parameter, so we don't need to check if the key exists. bool repeat = true; dict->GetBoolean(kConfigRuleHistogramRepeatKey, &repeat); int histogram_lower_value; int histogram_upper_value = std::numeric_limits::max(); if (!dict->GetInteger(kConfigRuleHistogramValue1Key, &histogram_lower_value)) { // Check for the old naming. if (!dict->GetInteger(kConfigRuleHistogramValueOldKey, &histogram_lower_value)) return nullptr; } dict->GetInteger(kConfigRuleHistogramValue2Key, &histogram_upper_value); if (histogram_lower_value >= histogram_upper_value) return nullptr; int trigger_delay = -1; dict->GetInteger(kConfigRuleTriggerDelay, &trigger_delay); return scoped_ptr( new HistogramRule(histogram_name, histogram_lower_value, histogram_upper_value, repeat, trigger_delay)); } ~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_lower_value_, histogram_upper_value_, repeat_)); TracingControllerImpl::GetInstance()->AddTraceMessageFilterObserver(this); } void IntoDict(base::DictionaryValue* dict) const override { DCHECK(dict); BackgroundTracingRule::IntoDict(dict); dict->SetString(kConfigRuleKey, kPreemptiveConfigRuleMonitorHistogram); dict->SetString(kConfigRuleHistogramNameKey, histogram_name_.c_str()); dict->SetInteger(kConfigRuleHistogramValue1Key, histogram_lower_value_); dict->SetInteger(kConfigRuleHistogramValue2Key, histogram_upper_value_); dict->SetBoolean(kConfigRuleHistogramRepeatKey, repeat_); if (trigger_delay_ != -1) dict->SetInteger(kConfigRuleTriggerDelay, trigger_delay_); } 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::OnRuleTriggered, base::Unretained(BackgroundTracingManagerImpl::GetInstance()), this, BackgroundTracingManager::StartedFinalizingCallback())); } void AbortTracing() { content::BrowserThread::PostTask( content::BrowserThread::UI, FROM_HERE, base::Bind( &BackgroundTracingManagerImpl::AbortScenario, base::Unretained(BackgroundTracingManagerImpl::GetInstance()))); } // TracingControllerImpl::TraceMessageFilterObserver implementation void OnTraceMessageFilterAdded(TraceMessageFilter* filter) override { filter->Send( new TracingMsg_SetUMACallback(histogram_name_, histogram_lower_value_, histogram_upper_value_, repeat_)); } void OnTraceMessageFilterRemoved(TraceMessageFilter* filter) override { filter->Send(new TracingMsg_ClearUMACallback(histogram_name_)); } void OnHistogramChangedCallback(const std::string& histogram_name, base::Histogram::Sample reference_lower_value, base::Histogram::Sample reference_upper_value, bool repeat, base::Histogram::Sample actual_value) { if (reference_lower_value > actual_value || reference_upper_value < actual_value) { if (!repeat) AbortTracing(); return; } OnHistogramTrigger(histogram_name); } bool ShouldTriggerNamedEvent(const std::string& named_event) const override { return named_event == histogram_name_; } int GetTraceTimeout() const override { return trigger_delay_; } private: std::string histogram_name_; int histogram_lower_value_; int histogram_upper_value_; bool repeat_; int trigger_delay_; }; class ReactiveTraceForNSOrTriggerOrFullRule : public BackgroundTracingRule { private: ReactiveTraceForNSOrTriggerOrFullRule( const std::string& named_event, BackgroundTracingConfigImpl::CategoryPreset category_preset) : named_event_(named_event), category_preset_(category_preset) {} public: static scoped_ptr Create( const base::DictionaryValue* dict, BackgroundTracingConfigImpl::CategoryPreset category_preset) { std::string trigger_name; if (!dict->GetString(kConfigRuleTriggerNameKey, &trigger_name)) return nullptr; return scoped_ptr( new ReactiveTraceForNSOrTriggerOrFullRule(trigger_name, category_preset)); } // BackgroundTracingRule implementation void IntoDict(base::DictionaryValue* dict) const override { DCHECK(dict); BackgroundTracingRule::IntoDict(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 GetTraceTimeout() 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 { private: 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(); } public: static scoped_ptr Create( const base::DictionaryValue* dict, BackgroundTracingConfigImpl::CategoryPreset category_preset) { 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)); } ~ReactiveTraceAtRandomIntervalsRule() override {} void IntoDict(base::DictionaryValue* dict) const override { DCHECK(dict); BackgroundTracingRule::IntoDict(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 GetTraceTimeout() 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 scoped_ptr BackgroundTracingRule::PreemptiveRuleFromDict( const base::DictionaryValue* dict) { DCHECK(dict); std::string type; if (!dict->GetString(kConfigRuleKey, &type)) return nullptr; scoped_ptr tracing_rule; if (type == kPreemptiveConfigRuleMonitorNamed) tracing_rule = NamedTriggerRule::Create(dict); else if (type == kPreemptiveConfigRuleMonitorHistogram) tracing_rule = HistogramRule::Create(dict); if (tracing_rule) tracing_rule->Setup(dict); return tracing_rule; } scoped_ptr BackgroundTracingRule::ReactiveRuleFromDict( const base::DictionaryValue* dict, BackgroundTracingConfigImpl::CategoryPreset category_preset) { DCHECK(dict); std::string type; if (!dict->GetString(kConfigRuleKey, &type)) return nullptr; scoped_ptr tracing_rule; if (type == kReactiveConfigRuleTraceOnNavigationUntilTriggerOrFull) { tracing_rule = ReactiveTraceForNSOrTriggerOrFullRule::Create(dict, category_preset); } else if (type == kReactiveConfigRuleTraceAtRandomIntervals) { tracing_rule = ReactiveTraceAtRandomIntervalsRule::Create(dict, category_preset); } if (tracing_rule) tracing_rule->Setup(dict); return tracing_rule; } } // namespace content