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"
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
);
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() {
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()),
88 requires_anonymized_data_(true),
89 trigger_handle_ids_(0) {
92 BackgroundTracingManagerImpl::~BackgroundTracingManagerImpl() {
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.
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
||
117 BackgroundTracingPreemptiveConfig::
118 MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE
) {
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
)
140 void BackgroundTracingManagerImpl::SetupUMACallbacks(
141 BackgroundTracingManagerImpl::SetupUMACallMode mode
) {
143 config_
->mode
!= BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
)
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
) {
157 if (mode
== CLEAR_CALLBACKS
) {
158 base::StatisticsRecorder::ClearCallback(
159 configs
[i
].histogram_trigger_info
.histogram_name
);
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
,
185 config_
->mode
== BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
);
187 if (reference_value
> actual_value
)
190 if (!is_tracing_
|| is_gathering_
)
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
);
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
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
)) {
220 base::MessageLoop::current()->PostTask(
222 base::Bind(&BackgroundTracingManagerImpl::ValidateStartupScenario
,
223 base::Unretained(this)));
226 if (!IsSupportedConfig(config
.get()))
229 // No point in tracing if there's nowhere to send it.
230 if (config
&& receive_callback
.is_null())
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
);
247 bool BackgroundTracingManagerImpl::HasActiveScenarioForTesting() {
251 void BackgroundTracingManagerImpl::ValidateStartupScenario() {
252 if (!config_
|| !delegate_
)
255 if (!delegate_
->IsAllowedToBeginBackgroundScenario(
256 *config_
.get(), requires_anonymized_data_
)) {
261 void BackgroundTracingManagerImpl::EnableRecordingIfConfigNeedsIt() {
265 if (config_
->mode
== BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
) {
266 EnableRecording(GetCategoryFilterStringForCategoryPreset(
267 static_cast<BackgroundTracingPreemptiveConfig
*>(config_
.get())
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 {
279 // If the last trace is still uploading, we don't allow a new one to trigger.
283 if (!IsTriggerHandleValid(handle
)) {
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
) {
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
)
315 if (trigger_name
== configs
[i
].trigger_name
) {
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
));
334 if (!IsAbleToTriggerTracing(handle
)) {
335 if (!callback
.is_null())
340 if (config_
->mode
== BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
) {
341 RecordBackgroundTracingMetric(PREEMPTIVE_TRIGGERED
);
342 BeginFinalizing(callback
);
344 RecordBackgroundTracingMetric(REACTIVE_TRIGGERED
);
346 tracing_timer_
->CancelTimer();
347 BeginFinalizing(callback
);
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
) {
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();
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)));
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.
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;
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()) {
507 if (base::JSONWriter::Write(*metadata_dict
.get(), &results
))
508 trace_data_sink
->SetMetadata(results
);
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() {
524 content::TracingController::GetInstance()->DisableRecording(nullptr);
528 BackgroundTracingManagerImpl::GetCategoryFilterStringForCategoryPreset(
529 BackgroundTracingConfig::CategoryPreset preset
) const {
531 case BackgroundTracingConfig::CategoryPreset::BENCHMARK
:
532 return "benchmark,toplevel";
533 case BackgroundTracingConfig::CategoryPreset::BENCHMARK_DEEP
:
534 return "*,disabled-by-default-benchmark.detailed";
540 } // namspace content