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"
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
);
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() {
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()),
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() {
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.
115 if (config
->mode
== BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
) {
116 BackgroundTracingPreemptiveConfig
* preemptive_config
=
117 static_cast<BackgroundTracingPreemptiveConfig
*>(config
);
118 const std::vector
<BackgroundTracingPreemptiveConfig::MonitoringRule
>&
119 configs
= preemptive_config
->configs
;
120 for (size_t i
= 0; i
< configs
.size(); ++i
) {
121 if (configs
[i
].type
== BackgroundTracingPreemptiveConfig::
122 MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED
||
124 BackgroundTracingPreemptiveConfig::
125 MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE
) {
132 if (config
->mode
== BackgroundTracingConfig::REACTIVE_TRACING_MODE
) {
133 BackgroundTracingReactiveConfig
* reactive_config
=
134 static_cast<BackgroundTracingReactiveConfig
*>(config
);
135 const std::vector
<BackgroundTracingReactiveConfig::TracingRule
>&
136 configs
= reactive_config
->configs
;
137 for (size_t i
= 0; i
< configs
.size(); ++i
) {
138 if (configs
[i
].type
!=
139 BackgroundTracingReactiveConfig::TRACE_FOR_10S_OR_TRIGGER_OR_FULL
)
147 void BackgroundTracingManagerImpl::SetupUMACallbacks(
148 BackgroundTracingManagerImpl::SetupUMACallMode mode
) {
150 config_
->mode
!= BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
)
153 BackgroundTracingPreemptiveConfig
* preemptive_config
=
154 static_cast<BackgroundTracingPreemptiveConfig
*>(config_
.get());
155 const std::vector
<BackgroundTracingPreemptiveConfig::MonitoringRule
>&
156 configs
= preemptive_config
->configs
;
157 for (size_t i
= 0; i
< configs
.size(); ++i
) {
158 if (configs
[i
].type
!=
159 BackgroundTracingPreemptiveConfig::
160 MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE
) {
164 if (mode
== CLEAR_CALLBACKS
) {
165 base::StatisticsRecorder::ClearCallback(
166 configs
[i
].histogram_trigger_info
.histogram_name
);
168 base::StatisticsRecorder::SetCallback(
169 configs
[i
].histogram_trigger_info
.histogram_name
,
170 base::Bind(&BackgroundTracingManagerImpl::OnHistogramChangedCallback
,
171 base::Unretained(this),
172 configs
[i
].histogram_trigger_info
.histogram_name
,
173 configs
[i
].histogram_trigger_info
.histogram_value
));
177 SetupFiltersFromConfig(mode
);
180 void BackgroundTracingManagerImpl::OnHistogramTrigger(
181 const std::string
& histogram_name
) {
182 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
)) {
183 content::BrowserThread::PostTask(
184 content::BrowserThread::UI
, FROM_HERE
,
185 base::Bind(&BackgroundTracingManagerImpl::OnHistogramTrigger
,
186 base::Unretained(this), histogram_name
));
191 config_
->mode
== BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
);
193 if (!is_tracing_
|| is_gathering_
)
196 BackgroundTracingPreemptiveConfig
* preemptive_config
=
197 static_cast<BackgroundTracingPreemptiveConfig
*>(config_
.get());
198 for (const auto& config
: preemptive_config
->configs
) {
199 if (config
.type
!= BackgroundTracingPreemptiveConfig::
200 MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE
) {
204 if (config
.histogram_trigger_info
.histogram_name
== histogram_name
) {
205 RecordBackgroundTracingMetric(PREEMPTIVE_TRIGGERED
);
206 BeginFinalizing(StartedFinalizingCallback());
211 void BackgroundTracingManagerImpl::OnHistogramChangedCallback(
212 const std::string
& histogram_name
,
213 base::Histogram::Sample reference_value
,
214 base::Histogram::Sample actual_value
) {
215 if (reference_value
> actual_value
)
218 OnHistogramTrigger(histogram_name
);
221 bool BackgroundTracingManagerImpl::SetActiveScenario(
222 scoped_ptr
<BackgroundTracingConfig
> config
,
223 const BackgroundTracingManager::ReceiveCallback
& receive_callback
,
224 DataFiltering data_filtering
) {
225 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
226 RecordBackgroundTracingMetric(SCENARIO_ACTIVATION_REQUESTED
);
231 bool requires_anonymized_data
= (data_filtering
== ANONYMIZE_DATA
);
233 // If the I/O thread isn't running, this is a startup scenario and
234 // we have to wait until initialization is finished to validate that the
236 if (BrowserThread::IsThreadInitialized(BrowserThread::IO
)) {
237 // TODO(oysteine): Retry when time_until_allowed has elapsed.
238 if (config
&& delegate_
&&
239 !delegate_
->IsAllowedToBeginBackgroundScenario(
240 *config
.get(), requires_anonymized_data
)) {
244 base::MessageLoop::current()->PostTask(
246 base::Bind(&BackgroundTracingManagerImpl::ValidateStartupScenario
,
247 base::Unretained(this)));
250 if (!IsSupportedConfig(config
.get()))
253 // No point in tracing if there's nowhere to send it.
254 if (config
&& receive_callback
.is_null())
257 SetupUMACallbacks(CLEAR_CALLBACKS
);
259 config_
= config
.Pass();
260 receive_callback_
= receive_callback
;
261 requires_anonymized_data_
= requires_anonymized_data
;
263 EnableRecordingIfConfigNeedsIt();
265 RecordBackgroundTracingMetric(SCENARIO_ACTIVATED_SUCCESSFULLY
);
269 bool BackgroundTracingManagerImpl::HasActiveScenarioForTesting() {
273 void BackgroundTracingManagerImpl::OnTraceMessageFilterAdded(
274 TraceMessageFilter
* filter
) {
275 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
276 SetupFilterFromConfig(filter
, BIND_CALLBACKS
);
279 void BackgroundTracingManagerImpl::SetupFiltersFromConfig(
280 BackgroundTracingManagerImpl::SetupUMACallMode mode
) {
281 TracingControllerImpl::TraceMessageFilterSet filters
;
282 TracingControllerImpl::GetInstance()->GetTraceMessageFilters(&filters
);
284 for (auto& filter
: filters
)
285 SetupFilterFromConfig(filter
, mode
);
288 void BackgroundTracingManagerImpl::SetupFilterFromConfig(
289 scoped_refptr
<TraceMessageFilter
> filter
,
290 BackgroundTracingManagerImpl::SetupUMACallMode mode
) {
292 config_
->mode
!= BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
)
295 BackgroundTracingPreemptiveConfig
* preemptive_config
=
296 static_cast<BackgroundTracingPreemptiveConfig
*>(config_
.get());
298 for (const auto& config
: preemptive_config
->configs
) {
299 if (config
.type
!= BackgroundTracingPreemptiveConfig::
300 MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE
) {
304 if (mode
== CLEAR_CALLBACKS
) {
305 filter
->Send(new TracingMsg_ClearUMACallback(
306 config
.histogram_trigger_info
.histogram_name
));
308 filter
->Send(new TracingMsg_SetUMACallback(
309 config
.histogram_trigger_info
.histogram_name
,
310 config
.histogram_trigger_info
.histogram_value
));
315 void BackgroundTracingManagerImpl::ValidateStartupScenario() {
316 if (!config_
|| !delegate_
)
319 if (!delegate_
->IsAllowedToBeginBackgroundScenario(
320 *config_
.get(), requires_anonymized_data_
)) {
325 void BackgroundTracingManagerImpl::EnableRecordingIfConfigNeedsIt() {
329 SetupUMACallbacks(BIND_CALLBACKS
);
331 if (config_
->mode
== BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
) {
332 EnableRecording(GetCategoryFilterStringForCategoryPreset(
333 static_cast<BackgroundTracingPreemptiveConfig
*>(config_
.get())
335 base::trace_event::RECORD_CONTINUOUSLY
);
337 // There is nothing to do in case of reactive tracing.
340 bool BackgroundTracingManagerImpl::IsAbleToTriggerTracing(
341 TriggerHandle handle
) const {
345 // If the last trace is still uploading, we don't allow a new one to trigger.
349 if (!IsTriggerHandleValid(handle
)) {
353 std::string trigger_name
= GetTriggerNameFromHandle(handle
);
355 if (config_
->mode
== BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
) {
356 BackgroundTracingPreemptiveConfig
* preemptive_config
=
357 static_cast<BackgroundTracingPreemptiveConfig
*>(config_
.get());
359 const std::vector
<BackgroundTracingPreemptiveConfig::MonitoringRule
>&
360 configs
= preemptive_config
->configs
;
362 for (size_t i
= 0; i
< configs
.size(); ++i
) {
363 if (configs
[i
].type
== BackgroundTracingPreemptiveConfig::
364 MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED
&&
365 configs
[i
].named_trigger_info
.trigger_name
== trigger_name
) {
370 BackgroundTracingReactiveConfig
* reactive_config
=
371 static_cast<BackgroundTracingReactiveConfig
*>(config_
.get());
373 const std::vector
<BackgroundTracingReactiveConfig::TracingRule
>&
374 configs
= reactive_config
->configs
;
376 for (size_t i
= 0; i
< configs
.size(); ++i
) {
377 if (configs
[i
].type
!=
378 BackgroundTracingReactiveConfig::
379 TRACE_FOR_10S_OR_TRIGGER_OR_FULL
)
381 if (trigger_name
== configs
[i
].trigger_name
) {
389 void BackgroundTracingManagerImpl::TriggerNamedEvent(
390 BackgroundTracingManagerImpl::TriggerHandle handle
,
391 StartedFinalizingCallback callback
) {
392 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
)) {
393 content::BrowserThread::PostTask(
394 content::BrowserThread::UI
, FROM_HERE
,
395 base::Bind(&BackgroundTracingManagerImpl::TriggerNamedEvent
,
396 base::Unretained(this), handle
, callback
));
400 if (!IsAbleToTriggerTracing(handle
)) {
401 if (!callback
.is_null())
406 if (config_
->mode
== BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
) {
407 RecordBackgroundTracingMetric(PREEMPTIVE_TRIGGERED
);
408 BeginFinalizing(callback
);
410 RecordBackgroundTracingMetric(REACTIVE_TRIGGERED
);
412 tracing_timer_
->CancelTimer();
413 BeginFinalizing(callback
);
417 // It was not already tracing, start a new trace.
418 BackgroundTracingReactiveConfig
* reactive_config
=
419 static_cast<BackgroundTracingReactiveConfig
*>(config_
.get());
420 const std::vector
<BackgroundTracingReactiveConfig::TracingRule
>&
421 configs
= reactive_config
->configs
;
422 std::string trigger_name
= GetTriggerNameFromHandle(handle
);
423 for (size_t i
= 0; i
< configs
.size(); ++i
) {
424 if (configs
[i
].trigger_name
== trigger_name
) {
426 GetCategoryFilterStringForCategoryPreset(
427 configs
[i
].category_preset
),
428 base::trace_event::RECORD_UNTIL_FULL
);
429 tracing_timer_
.reset(new TracingTimer(callback
));
430 tracing_timer_
->StartTimer();
437 BackgroundTracingManagerImpl::TriggerHandle
438 BackgroundTracingManagerImpl::RegisterTriggerType(const char* trigger_name
) {
439 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
441 trigger_handle_ids_
+= 1;
443 trigger_handles_
.insert(
444 std::pair
<TriggerHandle
, std::string
>(trigger_handle_ids_
, trigger_name
));
446 return static_cast<TriggerHandle
>(trigger_handle_ids_
);
449 bool BackgroundTracingManagerImpl::IsTriggerHandleValid(
450 BackgroundTracingManager::TriggerHandle handle
) const {
451 return trigger_handles_
.find(handle
) != trigger_handles_
.end();
454 std::string
BackgroundTracingManagerImpl::GetTriggerNameFromHandle(
455 BackgroundTracingManager::TriggerHandle handle
) const {
456 CHECK(IsTriggerHandleValid(handle
));
457 return trigger_handles_
.find(handle
)->second
;
460 void BackgroundTracingManagerImpl::GetTriggerNameList(
461 std::vector
<std::string
>* trigger_names
) {
462 for (std::map
<TriggerHandle
, std::string
>::iterator it
=
463 trigger_handles_
.begin();
464 it
!= trigger_handles_
.end(); ++it
)
465 trigger_names
->push_back(it
->second
);
468 void BackgroundTracingManagerImpl::InvalidateTriggerHandlesForTesting() {
469 trigger_handles_
.clear();
472 void BackgroundTracingManagerImpl::SetTracingEnabledCallbackForTesting(
473 const base::Closure
& callback
) {
474 tracing_enabled_callback_for_testing_
= callback
;
477 void BackgroundTracingManagerImpl::FireTimerForTesting() {
478 tracing_timer_
->FireTimerForTesting();
481 void BackgroundTracingManagerImpl::EnableRecording(
482 std::string category_filter_str
,
483 base::trace_event::TraceRecordMode record_mode
) {
484 base::trace_event::TraceConfig
trace_config(category_filter_str
, record_mode
);
485 if (requires_anonymized_data_
)
486 trace_config
.EnableArgumentFilter();
488 is_tracing_
= TracingController::GetInstance()->EnableRecording(
489 trace_config
, tracing_enabled_callback_for_testing_
);
490 RecordBackgroundTracingMetric(RECORDING_ENABLED
);
493 void BackgroundTracingManagerImpl::OnFinalizeStarted(
494 base::RefCountedString
* file_contents
) {
495 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
497 RecordBackgroundTracingMetric(FINALIZATION_STARTED
);
498 UMA_HISTOGRAM_MEMORY_KB("Tracing.Background.FinalizingTraceSizeInKB",
499 file_contents
->size() / 1024);
501 if (!receive_callback_
.is_null()) {
502 receive_callback_
.Run(
503 file_contents
, GenerateMetadataDict(),
504 base::Bind(&BackgroundTracingManagerImpl::OnFinalizeComplete
,
505 base::Unretained(this)));
509 void BackgroundTracingManagerImpl::OnFinalizeComplete() {
510 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
511 BrowserThread::PostTask(
512 BrowserThread::UI
, FROM_HERE
,
513 base::Bind(&BackgroundTracingManagerImpl::OnFinalizeComplete
,
514 base::Unretained(this)));
518 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
520 is_gathering_
= false;
522 if (!idle_callback_
.is_null())
523 idle_callback_
.Run();
525 // Now that a trace has completed, we may need to enable recording again.
526 // TODO(oysteine): Retry later if IsAllowedToBeginBackgroundScenario fails.
528 delegate_
->IsAllowedToBeginBackgroundScenario(
529 *config_
.get(), requires_anonymized_data_
)) {
530 EnableRecordingIfConfigNeedsIt();
532 // Clear all the callbacks so we don't keep hearing about histogram changes,
533 // etc. anymore, both in this process and in any child processes.
534 SetupUMACallbacks(CLEAR_CALLBACKS
);
537 RecordBackgroundTracingMetric(FINALIZATION_COMPLETE
);
540 scoped_ptr
<base::DictionaryValue
>
541 BackgroundTracingManagerImpl::GenerateMetadataDict() const {
542 // Grab the product version.
543 std::string product_version
= GetContentClient()->GetProduct();
545 // Serialize the config into json.
546 scoped_ptr
<base::DictionaryValue
> config_dict(new base::DictionaryValue());
548 BackgroundTracingConfig::IntoDict(config_
.get(), config_dict
.get());
550 scoped_ptr
<base::DictionaryValue
> metadata_dict(new base::DictionaryValue());
551 metadata_dict
->Set(kMetaDataConfigKey
, config_dict
.Pass());
552 metadata_dict
->SetString(kMetaDataVersionKey
, product_version
);
554 return metadata_dict
.Pass();
557 void BackgroundTracingManagerImpl::BeginFinalizing(
558 StartedFinalizingCallback callback
) {
559 is_gathering_
= true;
562 bool is_allowed_finalization
=
563 !delegate_
|| (config_
&&
564 delegate_
->IsAllowedToEndBackgroundScenario(
565 *config_
.get(), requires_anonymized_data_
));
567 scoped_refptr
<TracingControllerImpl::TraceDataSink
> trace_data_sink
;
568 if (is_allowed_finalization
) {
569 trace_data_sink
= content::TracingController::CreateCompressedStringSink(
570 content::TracingController::CreateCallbackEndpoint(
571 base::Bind(&BackgroundTracingManagerImpl::OnFinalizeStarted
,
572 base::Unretained(this))));
573 RecordBackgroundTracingMetric(FINALIZATION_ALLOWED
);
575 if (auto metadata_dict
= GenerateMetadataDict()) {
577 if (base::JSONWriter::Write(*metadata_dict
.get(), &results
))
578 trace_data_sink
->SetMetadata(results
);
581 RecordBackgroundTracingMetric(FINALIZATION_DISALLOWED
);
584 content::TracingController::GetInstance()->DisableRecording(trace_data_sink
);
586 if (!callback
.is_null())
587 callback
.Run(is_allowed_finalization
);
590 void BackgroundTracingManagerImpl::AbortScenario() {
591 SetupUMACallbacks(CLEAR_CALLBACKS
);
596 content::TracingController::GetInstance()->DisableRecording(nullptr);
600 BackgroundTracingManagerImpl::GetCategoryFilterStringForCategoryPreset(
601 BackgroundTracingConfig::CategoryPreset preset
) const {
603 case BackgroundTracingConfig::CategoryPreset::BENCHMARK
:
604 return "benchmark,toplevel";
605 case BackgroundTracingConfig::CategoryPreset::BENCHMARK_DEEP
:
606 return "*,disabled-by-default-benchmark.detailed";
612 } // namspace content