Add long running gmail memory benchmark for background tab.
[chromium-blink-merge.git] / content / browser / tracing / background_tracing_manager_impl.cc
blobd95b3344ed6f4d6687c6bf5b093f1b12e92fdc14
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 "components/tracing/tracing_messages.h"
13 #include "content/browser/tracing/trace_message_filter.h"
14 #include "content/public/browser/background_tracing_preemptive_config.h"
15 #include "content/public/browser/background_tracing_reactive_config.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/content_browser_client.h"
18 #include "content/public/browser/tracing_delegate.h"
19 #include "content/public/common/content_client.h"
21 namespace content {
23 namespace {
25 base::LazyInstance<BackgroundTracingManagerImpl>::Leaky g_controller =
26 LAZY_INSTANCE_INITIALIZER;
28 const char kMetaDataConfigKey[] = "config";
29 const char kMetaDataVersionKey[] = "product_version";
31 // These values are used for a histogram. Do not reorder.
32 enum BackgroundTracingMetrics {
33 SCENARIO_ACTIVATION_REQUESTED = 0,
34 SCENARIO_ACTIVATED_SUCCESSFULLY = 1,
35 RECORDING_ENABLED = 2,
36 PREEMPTIVE_TRIGGERED = 3,
37 REACTIVE_TRIGGERED = 4,
38 FINALIZATION_ALLOWED = 5,
39 FINALIZATION_DISALLOWED = 6,
40 FINALIZATION_STARTED = 7,
41 FINALIZATION_COMPLETE = 8,
42 NUMBER_OF_BACKGROUND_TRACING_METRICS,
45 void RecordBackgroundTracingMetric(BackgroundTracingMetrics metric) {
46 UMA_HISTOGRAM_ENUMERATION("Tracing.Background.ScenarioState", metric,
47 NUMBER_OF_BACKGROUND_TRACING_METRICS);
50 } // namespace
52 BackgroundTracingManagerImpl::TracingTimer::TracingTimer(
53 StartedFinalizingCallback callback) : callback_(callback) {
56 BackgroundTracingManagerImpl::TracingTimer::~TracingTimer() {
59 void BackgroundTracingManagerImpl::TracingTimer::StartTimer() {
60 const int kTimeoutSecs = 10;
61 tracing_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kTimeoutSecs),
62 this, &BackgroundTracingManagerImpl::TracingTimer::TracingTimerFired);
65 void BackgroundTracingManagerImpl::TracingTimer::CancelTimer() {
66 tracing_timer_.Stop();
69 void BackgroundTracingManagerImpl::TracingTimer::TracingTimerFired() {
70 BackgroundTracingManagerImpl::GetInstance()->BeginFinalizing(callback_);
73 void BackgroundTracingManagerImpl::TracingTimer::FireTimerForTesting() {
74 CancelTimer();
75 TracingTimerFired();
78 BackgroundTracingManager* BackgroundTracingManager::GetInstance() {
79 return BackgroundTracingManagerImpl::GetInstance();
82 BackgroundTracingManagerImpl* BackgroundTracingManagerImpl::GetInstance() {
83 return g_controller.Pointer();
86 BackgroundTracingManagerImpl::BackgroundTracingManagerImpl()
87 : delegate_(GetContentClient()->browser()->GetTracingDelegate()),
88 is_gathering_(false),
89 is_tracing_(false),
90 requires_anonymized_data_(true),
91 trigger_handle_ids_(0) {
92 // BackgroundTracingManagerImpl is leaky, so there's no danger of this being
93 // called after being destroyed and we can use base::Unretained().
94 TracingControllerImpl::GetInstance()->SetTraceMessageFilterAddedCallback(
95 base::Bind(&BackgroundTracingManagerImpl::OnTraceMessageFilterAdded,
96 base::Unretained(this)));
99 BackgroundTracingManagerImpl::~BackgroundTracingManagerImpl() {
100 NOTREACHED();
103 void BackgroundTracingManagerImpl::WhenIdle(
104 base::Callback<void()> idle_callback) {
105 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
106 idle_callback_ = idle_callback;
109 bool BackgroundTracingManagerImpl::IsSupportedConfig(
110 BackgroundTracingConfig* config) {
111 // No config is just fine, we just don't do anything.
112 if (!config)
113 return true;
115 if (config->mode == BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE) {
116 BackgroundTracingPreemptiveConfig* preemptive_config =
117 static_cast<BackgroundTracingPreemptiveConfig*>(config);
118 for (const auto& config : preemptive_config->configs) {
119 if (config.type == BackgroundTracingPreemptiveConfig::
120 MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED ||
121 config.type ==
122 BackgroundTracingPreemptiveConfig::
123 MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE) {
124 continue;
126 return false;
130 if (config->mode == BackgroundTracingConfig::REACTIVE_TRACING_MODE) {
131 BackgroundTracingReactiveConfig* reactive_config =
132 static_cast<BackgroundTracingReactiveConfig*>(config);
133 for (const auto& config : reactive_config->configs) {
134 if (config.type !=
135 BackgroundTracingReactiveConfig::TRACE_FOR_10S_OR_TRIGGER_OR_FULL)
136 return false;
140 return true;
143 void BackgroundTracingManagerImpl::SetupUMACallbacks(
144 BackgroundTracingManagerImpl::SetupUMACallMode mode) {
145 if (!config_ ||
146 config_->mode != BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE)
147 return;
149 BackgroundTracingPreemptiveConfig* preemptive_config =
150 static_cast<BackgroundTracingPreemptiveConfig*>(config_.get());
151 for (const auto& config : preemptive_config->configs) {
152 if (config.type != BackgroundTracingPreemptiveConfig::
153 MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE) {
154 continue;
157 if (mode == CLEAR_CALLBACKS) {
158 base::StatisticsRecorder::ClearCallback(
159 config.histogram_trigger_info.histogram_name);
160 } else {
161 base::StatisticsRecorder::SetCallback(
162 config.histogram_trigger_info.histogram_name,
163 base::Bind(&BackgroundTracingManagerImpl::OnHistogramChangedCallback,
164 base::Unretained(this),
165 config.histogram_trigger_info.histogram_name,
166 config.histogram_trigger_info.histogram_value));
170 SetupFiltersFromConfig(mode);
173 void BackgroundTracingManagerImpl::OnHistogramTrigger(
174 const std::string& histogram_name) {
175 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
176 content::BrowserThread::PostTask(
177 content::BrowserThread::UI, FROM_HERE,
178 base::Bind(&BackgroundTracingManagerImpl::OnHistogramTrigger,
179 base::Unretained(this), histogram_name));
180 return;
183 CHECK(config_ &&
184 config_->mode == BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE);
186 if (!is_tracing_ || is_gathering_)
187 return;
189 BackgroundTracingPreemptiveConfig* preemptive_config =
190 static_cast<BackgroundTracingPreemptiveConfig*>(config_.get());
191 for (const auto& config : preemptive_config->configs) {
192 if (config.type != BackgroundTracingPreemptiveConfig::
193 MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE) {
194 continue;
197 if (config.histogram_trigger_info.histogram_name == histogram_name) {
198 RecordBackgroundTracingMetric(PREEMPTIVE_TRIGGERED);
199 BeginFinalizing(StartedFinalizingCallback());
204 void BackgroundTracingManagerImpl::OnHistogramChangedCallback(
205 const std::string& histogram_name,
206 base::Histogram::Sample reference_value,
207 base::Histogram::Sample actual_value) {
208 if (reference_value > actual_value)
209 return;
211 OnHistogramTrigger(histogram_name);
214 bool BackgroundTracingManagerImpl::SetActiveScenario(
215 scoped_ptr<BackgroundTracingConfig> config,
216 const BackgroundTracingManager::ReceiveCallback& receive_callback,
217 DataFiltering data_filtering) {
218 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
219 RecordBackgroundTracingMetric(SCENARIO_ACTIVATION_REQUESTED);
221 if (is_tracing_)
222 return false;
224 bool requires_anonymized_data = (data_filtering == ANONYMIZE_DATA);
226 // If the I/O thread isn't running, this is a startup scenario and
227 // we have to wait until initialization is finished to validate that the
228 // scenario can run.
229 if (BrowserThread::IsThreadInitialized(BrowserThread::IO)) {
230 // TODO(oysteine): Retry when time_until_allowed has elapsed.
231 if (config && delegate_ &&
232 !delegate_->IsAllowedToBeginBackgroundScenario(
233 *config.get(), requires_anonymized_data)) {
234 return false;
236 } else {
237 base::MessageLoop::current()->PostTask(
238 FROM_HERE,
239 base::Bind(&BackgroundTracingManagerImpl::ValidateStartupScenario,
240 base::Unretained(this)));
243 if (!IsSupportedConfig(config.get()))
244 return false;
246 // No point in tracing if there's nowhere to send it.
247 if (config && receive_callback.is_null())
248 return false;
250 SetupUMACallbacks(CLEAR_CALLBACKS);
252 config_ = config.Pass();
253 receive_callback_ = receive_callback;
254 requires_anonymized_data_ = requires_anonymized_data;
256 EnableRecordingIfConfigNeedsIt();
258 RecordBackgroundTracingMetric(SCENARIO_ACTIVATED_SUCCESSFULLY);
259 return true;
262 bool BackgroundTracingManagerImpl::HasActiveScenarioForTesting() {
263 return config_;
266 void BackgroundTracingManagerImpl::OnTraceMessageFilterAdded(
267 TraceMessageFilter* filter) {
268 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
269 SetupFilterFromConfig(filter, BIND_CALLBACKS);
272 void BackgroundTracingManagerImpl::SetupFiltersFromConfig(
273 BackgroundTracingManagerImpl::SetupUMACallMode mode) {
274 TracingControllerImpl::TraceMessageFilterSet filters;
275 TracingControllerImpl::GetInstance()->GetTraceMessageFilters(&filters);
277 for (auto& filter : filters)
278 SetupFilterFromConfig(filter, mode);
281 void BackgroundTracingManagerImpl::SetupFilterFromConfig(
282 scoped_refptr<TraceMessageFilter> filter,
283 BackgroundTracingManagerImpl::SetupUMACallMode mode) {
284 if (!config_ ||
285 config_->mode != BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE)
286 return;
288 BackgroundTracingPreemptiveConfig* preemptive_config =
289 static_cast<BackgroundTracingPreemptiveConfig*>(config_.get());
291 for (const auto& config : preemptive_config->configs) {
292 if (config.type != BackgroundTracingPreemptiveConfig::
293 MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE) {
294 continue;
297 if (mode == CLEAR_CALLBACKS) {
298 filter->Send(new TracingMsg_ClearUMACallback(
299 config.histogram_trigger_info.histogram_name));
300 } else {
301 filter->Send(new TracingMsg_SetUMACallback(
302 config.histogram_trigger_info.histogram_name,
303 config.histogram_trigger_info.histogram_value));
308 void BackgroundTracingManagerImpl::ValidateStartupScenario() {
309 if (!config_ || !delegate_)
310 return;
312 if (!delegate_->IsAllowedToBeginBackgroundScenario(
313 *config_.get(), requires_anonymized_data_)) {
314 AbortScenario();
318 void BackgroundTracingManagerImpl::EnableRecordingIfConfigNeedsIt() {
319 if (!config_)
320 return;
322 SetupUMACallbacks(BIND_CALLBACKS);
324 if (config_->mode == BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE) {
325 EnableRecording(GetCategoryFilterStringForCategoryPreset(
326 static_cast<BackgroundTracingPreemptiveConfig*>(config_.get())
327 ->category_preset),
328 base::trace_event::RECORD_CONTINUOUSLY);
330 // There is nothing to do in case of reactive tracing.
333 bool BackgroundTracingManagerImpl::IsAbleToTriggerTracing(
334 TriggerHandle handle) const {
335 if (!config_)
336 return false;
338 // If the last trace is still uploading, we don't allow a new one to trigger.
339 if (is_gathering_)
340 return false;
342 if (!IsTriggerHandleValid(handle)) {
343 return false;
346 std::string trigger_name = GetTriggerNameFromHandle(handle);
348 if (config_->mode == BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE) {
349 BackgroundTracingPreemptiveConfig* preemptive_config =
350 static_cast<BackgroundTracingPreemptiveConfig*>(config_.get());
352 for (const auto& config : preemptive_config->configs) {
353 if (config.type == BackgroundTracingPreemptiveConfig::
354 MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED &&
355 config.named_trigger_info.trigger_name == trigger_name) {
356 return true;
359 } else {
360 BackgroundTracingReactiveConfig* reactive_config =
361 static_cast<BackgroundTracingReactiveConfig*>(config_.get());
363 for (const auto& config : reactive_config->configs) {
364 if (config.type !=
365 BackgroundTracingReactiveConfig::TRACE_FOR_10S_OR_TRIGGER_OR_FULL)
366 continue;
367 if (trigger_name == config.trigger_name) {
368 return true;
372 return false;
375 void BackgroundTracingManagerImpl::TriggerNamedEvent(
376 BackgroundTracingManagerImpl::TriggerHandle handle,
377 StartedFinalizingCallback callback) {
378 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
379 content::BrowserThread::PostTask(
380 content::BrowserThread::UI, FROM_HERE,
381 base::Bind(&BackgroundTracingManagerImpl::TriggerNamedEvent,
382 base::Unretained(this), handle, callback));
383 return;
386 if (!IsAbleToTriggerTracing(handle)) {
387 if (!callback.is_null())
388 callback.Run(false);
389 return;
392 if (config_->mode == BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE) {
393 RecordBackgroundTracingMetric(PREEMPTIVE_TRIGGERED);
394 BeginFinalizing(callback);
395 } else {
396 RecordBackgroundTracingMetric(REACTIVE_TRIGGERED);
397 if (is_tracing_) {
398 tracing_timer_->CancelTimer();
399 BeginFinalizing(callback);
400 return;
403 // It was not already tracing, start a new trace.
404 BackgroundTracingReactiveConfig* reactive_config =
405 static_cast<BackgroundTracingReactiveConfig*>(config_.get());
406 std::string trigger_name = GetTriggerNameFromHandle(handle);
407 for (const auto& config : reactive_config->configs) {
408 if (config.trigger_name == trigger_name) {
409 EnableRecording(
410 GetCategoryFilterStringForCategoryPreset(config.category_preset),
411 base::trace_event::RECORD_UNTIL_FULL);
412 tracing_timer_.reset(new TracingTimer(callback));
413 tracing_timer_->StartTimer();
414 break;
420 BackgroundTracingManagerImpl::TriggerHandle
421 BackgroundTracingManagerImpl::RegisterTriggerType(const char* trigger_name) {
422 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
424 trigger_handle_ids_ += 1;
426 trigger_handles_.insert(
427 std::pair<TriggerHandle, std::string>(trigger_handle_ids_, trigger_name));
429 return static_cast<TriggerHandle>(trigger_handle_ids_);
432 bool BackgroundTracingManagerImpl::IsTriggerHandleValid(
433 BackgroundTracingManager::TriggerHandle handle) const {
434 return trigger_handles_.find(handle) != trigger_handles_.end();
437 std::string BackgroundTracingManagerImpl::GetTriggerNameFromHandle(
438 BackgroundTracingManager::TriggerHandle handle) const {
439 CHECK(IsTriggerHandleValid(handle));
440 return trigger_handles_.find(handle)->second;
443 void BackgroundTracingManagerImpl::GetTriggerNameList(
444 std::vector<std::string>* trigger_names) {
445 for (const auto& it : trigger_handles_)
446 trigger_names->push_back(it.second);
449 void BackgroundTracingManagerImpl::InvalidateTriggerHandlesForTesting() {
450 trigger_handles_.clear();
453 void BackgroundTracingManagerImpl::SetTracingEnabledCallbackForTesting(
454 const base::Closure& callback) {
455 tracing_enabled_callback_for_testing_ = callback;
458 void BackgroundTracingManagerImpl::FireTimerForTesting() {
459 tracing_timer_->FireTimerForTesting();
462 void BackgroundTracingManagerImpl::EnableRecording(
463 std::string category_filter_str,
464 base::trace_event::TraceRecordMode record_mode) {
465 base::trace_event::TraceConfig trace_config(category_filter_str, record_mode);
466 if (requires_anonymized_data_)
467 trace_config.EnableArgumentFilter();
469 is_tracing_ = TracingController::GetInstance()->EnableRecording(
470 trace_config, tracing_enabled_callback_for_testing_);
471 RecordBackgroundTracingMetric(RECORDING_ENABLED);
474 void BackgroundTracingManagerImpl::OnFinalizeStarted(
475 base::RefCountedString* file_contents) {
476 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
478 RecordBackgroundTracingMetric(FINALIZATION_STARTED);
479 UMA_HISTOGRAM_MEMORY_KB("Tracing.Background.FinalizingTraceSizeInKB",
480 file_contents->size() / 1024);
482 if (!receive_callback_.is_null()) {
483 receive_callback_.Run(
484 file_contents, GenerateMetadataDict(),
485 base::Bind(&BackgroundTracingManagerImpl::OnFinalizeComplete,
486 base::Unretained(this)));
490 void BackgroundTracingManagerImpl::OnFinalizeComplete() {
491 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
492 BrowserThread::PostTask(
493 BrowserThread::UI, FROM_HERE,
494 base::Bind(&BackgroundTracingManagerImpl::OnFinalizeComplete,
495 base::Unretained(this)));
496 return;
499 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
501 is_gathering_ = false;
503 if (!idle_callback_.is_null())
504 idle_callback_.Run();
506 // Now that a trace has completed, we may need to enable recording again.
507 // TODO(oysteine): Retry later if IsAllowedToBeginBackgroundScenario fails.
508 if (!delegate_ ||
509 delegate_->IsAllowedToBeginBackgroundScenario(
510 *config_.get(), requires_anonymized_data_)) {
511 EnableRecordingIfConfigNeedsIt();
512 } else {
513 // Clear all the callbacks so we don't keep hearing about histogram changes,
514 // etc. anymore, both in this process and in any child processes.
515 SetupUMACallbacks(CLEAR_CALLBACKS);
518 RecordBackgroundTracingMetric(FINALIZATION_COMPLETE);
521 scoped_ptr<base::DictionaryValue>
522 BackgroundTracingManagerImpl::GenerateMetadataDict() const {
523 // Grab the product version.
524 std::string product_version = GetContentClient()->GetProduct();
526 // Serialize the config into json.
527 scoped_ptr<base::DictionaryValue> config_dict(new base::DictionaryValue());
529 BackgroundTracingConfig::IntoDict(config_.get(), config_dict.get());
531 scoped_ptr<base::DictionaryValue> metadata_dict(new base::DictionaryValue());
532 metadata_dict->Set(kMetaDataConfigKey, config_dict.Pass());
533 metadata_dict->SetString(kMetaDataVersionKey, product_version);
535 return metadata_dict.Pass();
538 void BackgroundTracingManagerImpl::BeginFinalizing(
539 StartedFinalizingCallback callback) {
540 is_gathering_ = true;
541 is_tracing_ = false;
543 bool is_allowed_finalization =
544 !delegate_ || (config_ &&
545 delegate_->IsAllowedToEndBackgroundScenario(
546 *config_.get(), requires_anonymized_data_));
548 scoped_refptr<TracingControllerImpl::TraceDataSink> trace_data_sink;
549 if (is_allowed_finalization) {
550 trace_data_sink = content::TracingController::CreateCompressedStringSink(
551 content::TracingController::CreateCallbackEndpoint(
552 base::Bind(&BackgroundTracingManagerImpl::OnFinalizeStarted,
553 base::Unretained(this))));
554 RecordBackgroundTracingMetric(FINALIZATION_ALLOWED);
556 if (auto metadata_dict = GenerateMetadataDict()) {
557 std::string results;
558 if (base::JSONWriter::Write(*metadata_dict.get(), &results))
559 trace_data_sink->SetMetadata(results);
561 } else {
562 RecordBackgroundTracingMetric(FINALIZATION_DISALLOWED);
565 content::TracingController::GetInstance()->DisableRecording(trace_data_sink);
567 if (!callback.is_null())
568 callback.Run(is_allowed_finalization);
571 void BackgroundTracingManagerImpl::AbortScenario() {
572 SetupUMACallbacks(CLEAR_CALLBACKS);
574 is_tracing_ = false;
575 config_.reset();
577 content::TracingController::GetInstance()->DisableRecording(nullptr);
580 std::string
581 BackgroundTracingManagerImpl::GetCategoryFilterStringForCategoryPreset(
582 BackgroundTracingConfig::CategoryPreset preset) const {
583 switch (preset) {
584 case BackgroundTracingConfig::CategoryPreset::BENCHMARK:
585 return "benchmark,toplevel";
586 case BackgroundTracingConfig::CategoryPreset::BENCHMARK_DEEP:
587 return "*,disabled-by-default-benchmark.detailed";
589 NOTREACHED();
590 return "";
593 } // namspace content