Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / content / browser / tracing / background_tracing_manager_impl.cc
blob68392b2c1e9f798b933b288169b672efb173426c
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/tracing/background_tracing_manager_impl.h"
7 #include "base/json/json_writer.h"
8 #include "base/macros.h"
9 #include "base/metrics/histogram_macros.h"
10 #include "base/metrics/statistics_recorder.h"
11 #include "base/time/time.h"
12 #include "content/public/browser/background_tracing_preemptive_config.h"
13 #include "content/public/browser/background_tracing_reactive_config.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/content_browser_client.h"
16 #include "content/public/browser/tracing_delegate.h"
17 #include "content/public/common/content_client.h"
19 namespace content {
21 namespace {
23 base::LazyInstance<BackgroundTracingManagerImpl>::Leaky g_controller =
24 LAZY_INSTANCE_INITIALIZER;
26 const char kMetaDataConfigKey[] = "config";
27 const char kMetaDataVersionKey[] = "product_version";
29 // These values are used for a histogram. Do not reorder.
30 enum BackgroundTracingMetrics {
31 SCENARIO_ACTIVATION_REQUESTED = 0,
32 SCENARIO_ACTIVATED_SUCCESSFULLY = 1,
33 RECORDING_ENABLED = 2,
34 PREEMPTIVE_TRIGGERED = 3,
35 REACTIVE_TRIGGERED = 4,
36 FINALIZATION_ALLOWED = 5,
37 FINALIZATION_DISALLOWED = 6,
38 FINALIZATION_STARTED = 7,
39 FINALIZATION_COMPLETE = 8,
40 NUMBER_OF_BACKGROUND_TRACING_METRICS,
43 void RecordBackgroundTracingMetric(BackgroundTracingMetrics metric) {
44 UMA_HISTOGRAM_ENUMERATION("Tracing.Background.ScenarioState", metric,
45 NUMBER_OF_BACKGROUND_TRACING_METRICS);
48 } // namespace
50 BackgroundTracingManagerImpl::TracingTimer::TracingTimer(
51 StartedFinalizingCallback callback) : callback_(callback) {
54 BackgroundTracingManagerImpl::TracingTimer::~TracingTimer() {
57 void BackgroundTracingManagerImpl::TracingTimer::StartTimer() {
58 const int kTimeoutSecs = 10;
59 tracing_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kTimeoutSecs),
60 this, &BackgroundTracingManagerImpl::TracingTimer::TracingTimerFired);
63 void BackgroundTracingManagerImpl::TracingTimer::CancelTimer() {
64 tracing_timer_.Stop();
67 void BackgroundTracingManagerImpl::TracingTimer::TracingTimerFired() {
68 BackgroundTracingManagerImpl::GetInstance()->BeginFinalizing(callback_);
71 void BackgroundTracingManagerImpl::TracingTimer::FireTimerForTesting() {
72 CancelTimer();
73 TracingTimerFired();
76 BackgroundTracingManager* BackgroundTracingManager::GetInstance() {
77 return BackgroundTracingManagerImpl::GetInstance();
80 BackgroundTracingManagerImpl* BackgroundTracingManagerImpl::GetInstance() {
81 return g_controller.Pointer();
84 BackgroundTracingManagerImpl::BackgroundTracingManagerImpl()
85 : delegate_(GetContentClient()->browser()->GetTracingDelegate()),
86 is_gathering_(false),
87 is_tracing_(false),
88 requires_anonymized_data_(true),
89 trigger_handle_ids_(0) {
92 BackgroundTracingManagerImpl::~BackgroundTracingManagerImpl() {
93 NOTREACHED();
96 void BackgroundTracingManagerImpl::WhenIdle(
97 base::Callback<void()> idle_callback) {
98 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
99 idle_callback_ = idle_callback;
102 bool BackgroundTracingManagerImpl::IsSupportedConfig(
103 BackgroundTracingConfig* config) {
104 // No config is just fine, we just don't do anything.
105 if (!config)
106 return true;
108 if (config->mode == BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE) {
109 BackgroundTracingPreemptiveConfig* preemptive_config =
110 static_cast<BackgroundTracingPreemptiveConfig*>(config);
111 const std::vector<BackgroundTracingPreemptiveConfig::MonitoringRule>&
112 configs = preemptive_config->configs;
113 for (size_t i = 0; i < configs.size(); ++i) {
114 if (configs[i].type == BackgroundTracingPreemptiveConfig::
115 MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED ||
116 configs[i].type ==
117 BackgroundTracingPreemptiveConfig::
118 MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE) {
119 continue;
121 return false;
125 if (config->mode == BackgroundTracingConfig::REACTIVE_TRACING_MODE) {
126 BackgroundTracingReactiveConfig* reactive_config =
127 static_cast<BackgroundTracingReactiveConfig*>(config);
128 const std::vector<BackgroundTracingReactiveConfig::TracingRule>&
129 configs = reactive_config->configs;
130 for (size_t i = 0; i < configs.size(); ++i) {
131 if (configs[i].type !=
132 BackgroundTracingReactiveConfig::TRACE_FOR_10S_OR_TRIGGER_OR_FULL)
133 return false;
137 return true;
140 void BackgroundTracingManagerImpl::SetupUMACallbacks(
141 BackgroundTracingManagerImpl::SetupUMACallMode mode) {
142 if (!config_ ||
143 config_->mode != BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE)
144 return;
146 BackgroundTracingPreemptiveConfig* preemptive_config =
147 static_cast<BackgroundTracingPreemptiveConfig*>(config_.get());
148 const std::vector<BackgroundTracingPreemptiveConfig::MonitoringRule>&
149 configs = preemptive_config->configs;
150 for (size_t i = 0; i < configs.size(); ++i) {
151 if (configs[i].type !=
152 BackgroundTracingPreemptiveConfig::
153 MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE) {
154 continue;
157 if (mode == CLEAR_CALLBACKS) {
158 base::StatisticsRecorder::ClearCallback(
159 configs[i].histogram_trigger_info.histogram_name);
160 } else {
161 base::StatisticsRecorder::SetCallback(
162 configs[i].histogram_trigger_info.histogram_name,
163 base::Bind(&BackgroundTracingManagerImpl::OnHistogramChanged,
164 base::Unretained(this),
165 configs[i].histogram_trigger_info.histogram_name,
166 configs[i].histogram_trigger_info.histogram_value));
171 void BackgroundTracingManagerImpl::OnHistogramChanged(
172 const std::string& histogram_name,
173 base::Histogram::Sample reference_value,
174 base::Histogram::Sample actual_value) {
175 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
176 content::BrowserThread::PostTask(
177 content::BrowserThread::UI, FROM_HERE,
178 base::Bind(&BackgroundTracingManagerImpl::OnHistogramChanged,
179 base::Unretained(this), histogram_name, reference_value,
180 actual_value));
181 return;
184 CHECK(config_ &&
185 config_->mode == BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE);
187 if (reference_value > actual_value)
188 return;
190 if (!is_tracing_ || is_gathering_)
191 return;
193 RecordBackgroundTracingMetric(PREEMPTIVE_TRIGGERED);
194 BeginFinalizing(StartedFinalizingCallback());
197 bool BackgroundTracingManagerImpl::SetActiveScenario(
198 scoped_ptr<BackgroundTracingConfig> config,
199 const BackgroundTracingManager::ReceiveCallback& receive_callback,
200 DataFiltering data_filtering) {
201 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
202 RecordBackgroundTracingMetric(SCENARIO_ACTIVATION_REQUESTED);
204 if (is_tracing_)
205 return false;
207 bool requires_anonymized_data = (data_filtering == ANONYMIZE_DATA);
209 // If the I/O thread isn't running, this is a startup scenario and
210 // we have to wait until initialization is finished to validate that the
211 // scenario can run.
212 if (BrowserThread::IsThreadInitialized(BrowserThread::IO)) {
213 // TODO(oysteine): Retry when time_until_allowed has elapsed.
214 if (config && delegate_ &&
215 !delegate_->IsAllowedToBeginBackgroundScenario(
216 *config.get(), requires_anonymized_data)) {
217 return false;
219 } else {
220 base::MessageLoop::current()->PostTask(
221 FROM_HERE,
222 base::Bind(&BackgroundTracingManagerImpl::ValidateStartupScenario,
223 base::Unretained(this)));
226 if (!IsSupportedConfig(config.get()))
227 return false;
229 // No point in tracing if there's nowhere to send it.
230 if (config && receive_callback.is_null())
231 return false;
233 SetupUMACallbacks(CLEAR_CALLBACKS);
235 config_ = config.Pass();
236 receive_callback_ = receive_callback;
237 requires_anonymized_data_ = requires_anonymized_data;
239 SetupUMACallbacks(BIND_CALLBACKS);
241 EnableRecordingIfConfigNeedsIt();
243 RecordBackgroundTracingMetric(SCENARIO_ACTIVATED_SUCCESSFULLY);
244 return true;
247 bool BackgroundTracingManagerImpl::HasActiveScenarioForTesting() {
248 return config_;
251 void BackgroundTracingManagerImpl::ValidateStartupScenario() {
252 if (!config_ || !delegate_)
253 return;
255 if (!delegate_->IsAllowedToBeginBackgroundScenario(
256 *config_.get(), requires_anonymized_data_)) {
257 AbortScenario();
261 void BackgroundTracingManagerImpl::EnableRecordingIfConfigNeedsIt() {
262 if (!config_)
263 return;
265 if (config_->mode == BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE) {
266 EnableRecording(GetCategoryFilterStringForCategoryPreset(
267 static_cast<BackgroundTracingPreemptiveConfig*>(config_.get())
268 ->category_preset),
269 base::trace_event::RECORD_CONTINUOUSLY);
271 // There is nothing to do in case of reactive tracing.
274 bool BackgroundTracingManagerImpl::IsAbleToTriggerTracing(
275 TriggerHandle handle) const {
276 if (!config_)
277 return false;
279 // If the last trace is still uploading, we don't allow a new one to trigger.
280 if (is_gathering_)
281 return false;
283 if (!IsTriggerHandleValid(handle)) {
284 return false;
287 std::string trigger_name = GetTriggerNameFromHandle(handle);
289 if (config_->mode == BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE) {
290 BackgroundTracingPreemptiveConfig* preemptive_config =
291 static_cast<BackgroundTracingPreemptiveConfig*>(config_.get());
293 const std::vector<BackgroundTracingPreemptiveConfig::MonitoringRule>&
294 configs = preemptive_config->configs;
296 for (size_t i = 0; i < configs.size(); ++i) {
297 if (configs[i].type == BackgroundTracingPreemptiveConfig::
298 MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED &&
299 configs[i].named_trigger_info.trigger_name == trigger_name) {
300 return true;
303 } else {
304 BackgroundTracingReactiveConfig* reactive_config =
305 static_cast<BackgroundTracingReactiveConfig*>(config_.get());
307 const std::vector<BackgroundTracingReactiveConfig::TracingRule>&
308 configs = reactive_config->configs;
310 for (size_t i = 0; i < configs.size(); ++i) {
311 if (configs[i].type !=
312 BackgroundTracingReactiveConfig::
313 TRACE_FOR_10S_OR_TRIGGER_OR_FULL)
314 continue;
315 if (trigger_name == configs[i].trigger_name) {
316 return true;
320 return false;
323 void BackgroundTracingManagerImpl::TriggerNamedEvent(
324 BackgroundTracingManagerImpl::TriggerHandle handle,
325 StartedFinalizingCallback callback) {
326 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
327 content::BrowserThread::PostTask(
328 content::BrowserThread::UI, FROM_HERE,
329 base::Bind(&BackgroundTracingManagerImpl::TriggerNamedEvent,
330 base::Unretained(this), handle, callback));
331 return;
334 if (!IsAbleToTriggerTracing(handle)) {
335 if (!callback.is_null())
336 callback.Run(false);
337 return;
340 if (config_->mode == BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE) {
341 RecordBackgroundTracingMetric(PREEMPTIVE_TRIGGERED);
342 BeginFinalizing(callback);
343 } else {
344 RecordBackgroundTracingMetric(REACTIVE_TRIGGERED);
345 if (is_tracing_) {
346 tracing_timer_->CancelTimer();
347 BeginFinalizing(callback);
348 return;
351 // It was not already tracing, start a new trace.
352 BackgroundTracingReactiveConfig* reactive_config =
353 static_cast<BackgroundTracingReactiveConfig*>(config_.get());
354 const std::vector<BackgroundTracingReactiveConfig::TracingRule>&
355 configs = reactive_config->configs;
356 std::string trigger_name = GetTriggerNameFromHandle(handle);
357 for (size_t i = 0; i < configs.size(); ++i) {
358 if (configs[i].trigger_name == trigger_name) {
359 EnableRecording(
360 GetCategoryFilterStringForCategoryPreset(
361 configs[i].category_preset),
362 base::trace_event::RECORD_UNTIL_FULL);
363 tracing_timer_.reset(new TracingTimer(callback));
364 tracing_timer_->StartTimer();
365 break;
371 BackgroundTracingManagerImpl::TriggerHandle
372 BackgroundTracingManagerImpl::RegisterTriggerType(const char* trigger_name) {
373 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
375 trigger_handle_ids_ += 1;
377 trigger_handles_.insert(
378 std::pair<TriggerHandle, std::string>(trigger_handle_ids_, trigger_name));
380 return static_cast<TriggerHandle>(trigger_handle_ids_);
383 bool BackgroundTracingManagerImpl::IsTriggerHandleValid(
384 BackgroundTracingManager::TriggerHandle handle) const {
385 return trigger_handles_.find(handle) != trigger_handles_.end();
388 std::string BackgroundTracingManagerImpl::GetTriggerNameFromHandle(
389 BackgroundTracingManager::TriggerHandle handle) const {
390 CHECK(IsTriggerHandleValid(handle));
391 return trigger_handles_.find(handle)->second;
394 void BackgroundTracingManagerImpl::GetTriggerNameList(
395 std::vector<std::string>* trigger_names) {
396 for (std::map<TriggerHandle, std::string>::iterator it =
397 trigger_handles_.begin();
398 it != trigger_handles_.end(); ++it)
399 trigger_names->push_back(it->second);
402 void BackgroundTracingManagerImpl::InvalidateTriggerHandlesForTesting() {
403 trigger_handles_.clear();
406 void BackgroundTracingManagerImpl::SetTracingEnabledCallbackForTesting(
407 const base::Closure& callback) {
408 tracing_enabled_callback_for_testing_ = callback;
411 void BackgroundTracingManagerImpl::FireTimerForTesting() {
412 tracing_timer_->FireTimerForTesting();
415 void BackgroundTracingManagerImpl::EnableRecording(
416 std::string category_filter_str,
417 base::trace_event::TraceRecordMode record_mode) {
418 base::trace_event::TraceConfig trace_config(category_filter_str, record_mode);
419 if (requires_anonymized_data_)
420 trace_config.EnableArgumentFilter();
422 is_tracing_ = TracingController::GetInstance()->EnableRecording(
423 trace_config, tracing_enabled_callback_for_testing_);
424 RecordBackgroundTracingMetric(RECORDING_ENABLED);
427 void BackgroundTracingManagerImpl::OnFinalizeStarted(
428 base::RefCountedString* file_contents) {
429 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
431 RecordBackgroundTracingMetric(FINALIZATION_STARTED);
432 UMA_HISTOGRAM_MEMORY_KB("Tracing.Background.FinalizingTraceSizeInKB",
433 file_contents->size() / 1024);
435 if (!receive_callback_.is_null()) {
436 receive_callback_.Run(
437 file_contents, GenerateMetadataDict(),
438 base::Bind(&BackgroundTracingManagerImpl::OnFinalizeComplete,
439 base::Unretained(this)));
443 void BackgroundTracingManagerImpl::OnFinalizeComplete() {
444 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
445 BrowserThread::PostTask(
446 BrowserThread::UI, FROM_HERE,
447 base::Bind(&BackgroundTracingManagerImpl::OnFinalizeComplete,
448 base::Unretained(this)));
449 return;
452 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
454 is_gathering_ = false;
456 if (!idle_callback_.is_null())
457 idle_callback_.Run();
459 // Now that a trace has completed, we may need to enable recording again.
460 // TODO(oysteine): Retry later if IsAllowedToBeginBackgroundScenario fails.
461 if (!delegate_ ||
462 delegate_->IsAllowedToBeginBackgroundScenario(
463 *config_.get(), requires_anonymized_data_)) {
464 EnableRecordingIfConfigNeedsIt();
467 RecordBackgroundTracingMetric(FINALIZATION_COMPLETE);
470 scoped_ptr<base::DictionaryValue>
471 BackgroundTracingManagerImpl::GenerateMetadataDict() const {
472 // Grab the product version.
473 std::string product_version = GetContentClient()->GetProduct();
475 // Serialize the config into json.
476 scoped_ptr<base::DictionaryValue> config_dict(new base::DictionaryValue());
478 BackgroundTracingConfig::IntoDict(config_.get(), config_dict.get());
480 scoped_ptr<base::DictionaryValue> metadata_dict(new base::DictionaryValue());
481 metadata_dict->Set(kMetaDataConfigKey, config_dict.Pass());
482 metadata_dict->SetString(kMetaDataVersionKey, product_version);
484 return metadata_dict.Pass();
487 void BackgroundTracingManagerImpl::BeginFinalizing(
488 StartedFinalizingCallback callback) {
489 is_gathering_ = true;
490 is_tracing_ = false;
492 bool is_allowed_finalization =
493 !delegate_ || (config_ &&
494 delegate_->IsAllowedToEndBackgroundScenario(
495 *config_.get(), requires_anonymized_data_));
497 scoped_refptr<TracingControllerImpl::TraceDataSink> trace_data_sink;
498 if (is_allowed_finalization) {
499 trace_data_sink = content::TracingController::CreateCompressedStringSink(
500 content::TracingController::CreateCallbackEndpoint(
501 base::Bind(&BackgroundTracingManagerImpl::OnFinalizeStarted,
502 base::Unretained(this))));
503 RecordBackgroundTracingMetric(FINALIZATION_ALLOWED);
505 if (auto metadata_dict = GenerateMetadataDict()) {
506 std::string results;
507 if (base::JSONWriter::Write(*metadata_dict.get(), &results))
508 trace_data_sink->SetMetadata(results);
510 } else {
511 RecordBackgroundTracingMetric(FINALIZATION_DISALLOWED);
514 content::TracingController::GetInstance()->DisableRecording(trace_data_sink);
516 if (!callback.is_null())
517 callback.Run(is_allowed_finalization);
520 void BackgroundTracingManagerImpl::AbortScenario() {
521 is_tracing_ = false;
522 config_.reset();
524 content::TracingController::GetInstance()->DisableRecording(nullptr);
527 std::string
528 BackgroundTracingManagerImpl::GetCategoryFilterStringForCategoryPreset(
529 BackgroundTracingConfig::CategoryPreset preset) const {
530 switch (preset) {
531 case BackgroundTracingConfig::CategoryPreset::BENCHMARK:
532 return "benchmark,toplevel";
533 case BackgroundTracingConfig::CategoryPreset::BENCHMARK_DEEP:
534 return "*,disabled-by-default-benchmark.detailed";
536 NOTREACHED();
537 return "";
540 } // namspace content