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"
20 #include "net/base/network_change_notifier.h"
26 base::LazyInstance
<BackgroundTracingManagerImpl
>::Leaky g_controller
=
27 LAZY_INSTANCE_INITIALIZER
;
29 const char kMetaDataConfigKey
[] = "config";
30 const char kMetaDataNetworkType
[] = "network_type";
31 const char kMetaDataVersionKey
[] = "product_version";
33 // These values are used for a histogram. Do not reorder.
34 enum BackgroundTracingMetrics
{
35 SCENARIO_ACTIVATION_REQUESTED
= 0,
36 SCENARIO_ACTIVATED_SUCCESSFULLY
= 1,
37 RECORDING_ENABLED
= 2,
38 PREEMPTIVE_TRIGGERED
= 3,
39 REACTIVE_TRIGGERED
= 4,
40 FINALIZATION_ALLOWED
= 5,
41 FINALIZATION_DISALLOWED
= 6,
42 FINALIZATION_STARTED
= 7,
43 FINALIZATION_COMPLETE
= 8,
44 NUMBER_OF_BACKGROUND_TRACING_METRICS
,
47 void RecordBackgroundTracingMetric(BackgroundTracingMetrics metric
) {
48 UMA_HISTOGRAM_ENUMERATION("Tracing.Background.ScenarioState", metric
,
49 NUMBER_OF_BACKGROUND_TRACING_METRICS
);
52 std::string
GetNetworkTypeString() {
53 switch (net::NetworkChangeNotifier::GetConnectionType()) {
54 case net::NetworkChangeNotifier::CONNECTION_ETHERNET
:
56 case net::NetworkChangeNotifier::CONNECTION_WIFI
:
58 case net::NetworkChangeNotifier::CONNECTION_2G
:
60 case net::NetworkChangeNotifier::CONNECTION_3G
:
62 case net::NetworkChangeNotifier::CONNECTION_4G
:
64 case net::NetworkChangeNotifier::CONNECTION_NONE
:
66 case net::NetworkChangeNotifier::CONNECTION_BLUETOOTH
:
68 case net::NetworkChangeNotifier::CONNECTION_UNKNOWN
:
77 BackgroundTracingManagerImpl::TracingTimer::TracingTimer(
78 StartedFinalizingCallback callback
) : callback_(callback
) {
81 BackgroundTracingManagerImpl::TracingTimer::~TracingTimer() {
84 void BackgroundTracingManagerImpl::TracingTimer::StartTimer() {
85 const int kTimeoutSecs
= 10;
86 tracing_timer_
.Start(FROM_HERE
, base::TimeDelta::FromSeconds(kTimeoutSecs
),
87 this, &BackgroundTracingManagerImpl::TracingTimer::TracingTimerFired
);
90 void BackgroundTracingManagerImpl::TracingTimer::CancelTimer() {
91 tracing_timer_
.Stop();
94 void BackgroundTracingManagerImpl::TracingTimer::TracingTimerFired() {
95 BackgroundTracingManagerImpl::GetInstance()->BeginFinalizing(callback_
);
98 void BackgroundTracingManagerImpl::TracingTimer::FireTimerForTesting() {
103 BackgroundTracingManager
* BackgroundTracingManager::GetInstance() {
104 return BackgroundTracingManagerImpl::GetInstance();
107 BackgroundTracingManagerImpl
* BackgroundTracingManagerImpl::GetInstance() {
108 return g_controller
.Pointer();
111 BackgroundTracingManagerImpl::BackgroundTracingManagerImpl()
112 : delegate_(GetContentClient()->browser()->GetTracingDelegate()),
113 is_gathering_(false),
115 requires_anonymized_data_(true),
116 trigger_handle_ids_(0) {
117 // BackgroundTracingManagerImpl is leaky, so there's no danger of this being
118 // called after being destroyed and we can use base::Unretained().
119 TracingControllerImpl::GetInstance()->SetTraceMessageFilterAddedCallback(
120 base::Bind(&BackgroundTracingManagerImpl::OnTraceMessageFilterAdded
,
121 base::Unretained(this)));
124 BackgroundTracingManagerImpl::~BackgroundTracingManagerImpl() {
128 void BackgroundTracingManagerImpl::WhenIdle(
129 base::Callback
<void()> idle_callback
) {
130 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
131 idle_callback_
= idle_callback
;
134 bool BackgroundTracingManagerImpl::IsSupportedConfig(
135 BackgroundTracingConfig
* config
) {
136 // No config is just fine, we just don't do anything.
140 if (config
->mode
== BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
) {
141 for (const auto& preemptive_config
:
142 static_cast<BackgroundTracingPreemptiveConfig
*>(config
)->configs
) {
143 if (preemptive_config
.type
== BackgroundTracingPreemptiveConfig::
144 MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED
||
145 preemptive_config
.type
==
146 BackgroundTracingPreemptiveConfig::
147 MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE
) {
154 if (config
->mode
== BackgroundTracingConfig::REACTIVE_TRACING_MODE
) {
155 for (const auto& reactive_config
:
156 static_cast<BackgroundTracingReactiveConfig
*>(config
)->configs
) {
157 if (reactive_config
.type
!=
158 BackgroundTracingReactiveConfig::TRACE_FOR_10S_OR_TRIGGER_OR_FULL
)
166 void BackgroundTracingManagerImpl::SetupUMACallbacks(
167 BackgroundTracingManagerImpl::SetupUMACallMode mode
) {
169 config_
->mode
!= BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
)
172 BackgroundTracingPreemptiveConfig
* preemptive_config
=
173 static_cast<BackgroundTracingPreemptiveConfig
*>(config_
.get());
174 for (const auto& config
: preemptive_config
->configs
) {
175 if (config
.type
!= BackgroundTracingPreemptiveConfig::
176 MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE
) {
180 if (mode
== CLEAR_CALLBACKS
) {
181 base::StatisticsRecorder::ClearCallback(
182 config
.histogram_trigger_info
.histogram_name
);
184 base::StatisticsRecorder::SetCallback(
185 config
.histogram_trigger_info
.histogram_name
,
186 base::Bind(&BackgroundTracingManagerImpl::OnHistogramChangedCallback
,
187 base::Unretained(this),
188 config
.histogram_trigger_info
.histogram_name
,
189 config
.histogram_trigger_info
.histogram_value
));
193 SetupFiltersFromConfig(mode
);
196 void BackgroundTracingManagerImpl::OnHistogramTrigger(
197 const std::string
& histogram_name
) {
198 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
)) {
199 content::BrowserThread::PostTask(
200 content::BrowserThread::UI
, FROM_HERE
,
201 base::Bind(&BackgroundTracingManagerImpl::OnHistogramTrigger
,
202 base::Unretained(this), histogram_name
));
207 config_
->mode
== BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
);
209 if (!is_tracing_
|| is_gathering_
)
212 BackgroundTracingPreemptiveConfig
* preemptive_config
=
213 static_cast<BackgroundTracingPreemptiveConfig
*>(config_
.get());
214 for (const auto& config
: preemptive_config
->configs
) {
215 if (config
.type
!= BackgroundTracingPreemptiveConfig::
216 MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE
) {
220 if (config
.histogram_trigger_info
.histogram_name
== histogram_name
) {
221 RecordBackgroundTracingMetric(PREEMPTIVE_TRIGGERED
);
222 BeginFinalizing(StartedFinalizingCallback());
227 void BackgroundTracingManagerImpl::OnHistogramChangedCallback(
228 const std::string
& histogram_name
,
229 base::Histogram::Sample reference_value
,
230 base::Histogram::Sample actual_value
) {
231 if (reference_value
> actual_value
)
234 OnHistogramTrigger(histogram_name
);
237 bool BackgroundTracingManagerImpl::SetActiveScenario(
238 scoped_ptr
<BackgroundTracingConfig
> config
,
239 const BackgroundTracingManager::ReceiveCallback
& receive_callback
,
240 DataFiltering data_filtering
) {
241 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
242 RecordBackgroundTracingMetric(SCENARIO_ACTIVATION_REQUESTED
);
247 bool requires_anonymized_data
= (data_filtering
== ANONYMIZE_DATA
);
249 // If the I/O thread isn't running, this is a startup scenario and
250 // we have to wait until initialization is finished to validate that the
252 if (BrowserThread::IsThreadInitialized(BrowserThread::IO
)) {
253 // TODO(oysteine): Retry when time_until_allowed has elapsed.
254 if (config
&& delegate_
&&
255 !delegate_
->IsAllowedToBeginBackgroundScenario(
256 *config
.get(), requires_anonymized_data
)) {
260 base::MessageLoop::current()->PostTask(
262 base::Bind(&BackgroundTracingManagerImpl::ValidateStartupScenario
,
263 base::Unretained(this)));
266 if (!IsSupportedConfig(config
.get()))
269 // No point in tracing if there's nowhere to send it.
270 if (config
&& receive_callback
.is_null())
273 SetupUMACallbacks(CLEAR_CALLBACKS
);
275 config_
= config
.Pass();
276 receive_callback_
= receive_callback
;
277 requires_anonymized_data_
= requires_anonymized_data
;
279 EnableRecordingIfConfigNeedsIt();
281 RecordBackgroundTracingMetric(SCENARIO_ACTIVATED_SUCCESSFULLY
);
285 bool BackgroundTracingManagerImpl::HasActiveScenarioForTesting() {
289 void BackgroundTracingManagerImpl::OnTraceMessageFilterAdded(
290 TraceMessageFilter
* filter
) {
291 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
292 SetupFilterFromConfig(filter
, BIND_CALLBACKS
);
295 void BackgroundTracingManagerImpl::SetupFiltersFromConfig(
296 BackgroundTracingManagerImpl::SetupUMACallMode mode
) {
297 TracingControllerImpl::TraceMessageFilterSet filters
;
298 TracingControllerImpl::GetInstance()->GetTraceMessageFilters(&filters
);
300 for (auto& filter
: filters
)
301 SetupFilterFromConfig(filter
, mode
);
304 void BackgroundTracingManagerImpl::SetupFilterFromConfig(
305 scoped_refptr
<TraceMessageFilter
> filter
,
306 BackgroundTracingManagerImpl::SetupUMACallMode mode
) {
308 config_
->mode
!= BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
)
311 BackgroundTracingPreemptiveConfig
* preemptive_config
=
312 static_cast<BackgroundTracingPreemptiveConfig
*>(config_
.get());
314 for (const auto& config
: preemptive_config
->configs
) {
315 if (config
.type
!= BackgroundTracingPreemptiveConfig::
316 MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE
) {
320 if (mode
== CLEAR_CALLBACKS
) {
321 filter
->Send(new TracingMsg_ClearUMACallback(
322 config
.histogram_trigger_info
.histogram_name
));
324 filter
->Send(new TracingMsg_SetUMACallback(
325 config
.histogram_trigger_info
.histogram_name
,
326 config
.histogram_trigger_info
.histogram_value
));
331 void BackgroundTracingManagerImpl::ValidateStartupScenario() {
332 if (!config_
|| !delegate_
)
335 if (!delegate_
->IsAllowedToBeginBackgroundScenario(
336 *config_
.get(), requires_anonymized_data_
)) {
341 void BackgroundTracingManagerImpl::EnableRecordingIfConfigNeedsIt() {
345 SetupUMACallbacks(BIND_CALLBACKS
);
347 if (config_
->mode
== BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
) {
348 EnableRecording(GetCategoryFilterStringForCategoryPreset(
349 static_cast<BackgroundTracingPreemptiveConfig
*>(config_
.get())
351 base::trace_event::RECORD_CONTINUOUSLY
);
353 // There is nothing to do in case of reactive tracing.
356 bool BackgroundTracingManagerImpl::IsAbleToTriggerTracing(
357 TriggerHandle handle
) const {
361 // If the last trace is still uploading, we don't allow a new one to trigger.
365 if (!IsTriggerHandleValid(handle
)) {
369 std::string trigger_name
= GetTriggerNameFromHandle(handle
);
371 if (config_
->mode
== BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
) {
372 BackgroundTracingPreemptiveConfig
* preemptive_config
=
373 static_cast<BackgroundTracingPreemptiveConfig
*>(config_
.get());
375 for (const auto& config
: preemptive_config
->configs
) {
376 if (config
.type
== BackgroundTracingPreemptiveConfig::
377 MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED
&&
378 config
.named_trigger_info
.trigger_name
== trigger_name
) {
383 BackgroundTracingReactiveConfig
* reactive_config
=
384 static_cast<BackgroundTracingReactiveConfig
*>(config_
.get());
386 for (const auto& config
: reactive_config
->configs
) {
388 BackgroundTracingReactiveConfig::TRACE_FOR_10S_OR_TRIGGER_OR_FULL
)
390 if (trigger_name
== config
.trigger_name
) {
398 void BackgroundTracingManagerImpl::TriggerNamedEvent(
399 BackgroundTracingManagerImpl::TriggerHandle handle
,
400 StartedFinalizingCallback callback
) {
401 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
)) {
402 content::BrowserThread::PostTask(
403 content::BrowserThread::UI
, FROM_HERE
,
404 base::Bind(&BackgroundTracingManagerImpl::TriggerNamedEvent
,
405 base::Unretained(this), handle
, callback
));
409 if (!IsAbleToTriggerTracing(handle
)) {
410 if (!callback
.is_null())
415 if (config_
->mode
== BackgroundTracingConfig::PREEMPTIVE_TRACING_MODE
) {
416 RecordBackgroundTracingMetric(PREEMPTIVE_TRIGGERED
);
417 BeginFinalizing(callback
);
419 RecordBackgroundTracingMetric(REACTIVE_TRIGGERED
);
421 tracing_timer_
->CancelTimer();
422 BeginFinalizing(callback
);
426 // It was not already tracing, start a new trace.
427 BackgroundTracingReactiveConfig
* reactive_config
=
428 static_cast<BackgroundTracingReactiveConfig
*>(config_
.get());
429 std::string trigger_name
= GetTriggerNameFromHandle(handle
);
430 for (const auto& config
: reactive_config
->configs
) {
431 if (config
.trigger_name
== trigger_name
) {
433 GetCategoryFilterStringForCategoryPreset(config
.category_preset
),
434 base::trace_event::RECORD_UNTIL_FULL
);
435 tracing_timer_
.reset(new TracingTimer(callback
));
436 tracing_timer_
->StartTimer();
443 BackgroundTracingManagerImpl::TriggerHandle
444 BackgroundTracingManagerImpl::RegisterTriggerType(const char* trigger_name
) {
445 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
447 trigger_handle_ids_
+= 1;
449 trigger_handles_
.insert(
450 std::pair
<TriggerHandle
, std::string
>(trigger_handle_ids_
, trigger_name
));
452 return static_cast<TriggerHandle
>(trigger_handle_ids_
);
455 bool BackgroundTracingManagerImpl::IsTriggerHandleValid(
456 BackgroundTracingManager::TriggerHandle handle
) const {
457 return trigger_handles_
.find(handle
) != trigger_handles_
.end();
460 std::string
BackgroundTracingManagerImpl::GetTriggerNameFromHandle(
461 BackgroundTracingManager::TriggerHandle handle
) const {
462 CHECK(IsTriggerHandleValid(handle
));
463 return trigger_handles_
.find(handle
)->second
;
466 void BackgroundTracingManagerImpl::GetTriggerNameList(
467 std::vector
<std::string
>* trigger_names
) {
468 for (const auto& it
: trigger_handles_
)
469 trigger_names
->push_back(it
.second
);
472 void BackgroundTracingManagerImpl::InvalidateTriggerHandlesForTesting() {
473 trigger_handles_
.clear();
476 void BackgroundTracingManagerImpl::SetTracingEnabledCallbackForTesting(
477 const base::Closure
& callback
) {
478 tracing_enabled_callback_for_testing_
= callback
;
481 void BackgroundTracingManagerImpl::FireTimerForTesting() {
482 tracing_timer_
->FireTimerForTesting();
485 void BackgroundTracingManagerImpl::EnableRecording(
486 std::string category_filter_str
,
487 base::trace_event::TraceRecordMode record_mode
) {
488 base::trace_event::TraceConfig
trace_config(category_filter_str
, record_mode
);
489 if (requires_anonymized_data_
)
490 trace_config
.EnableArgumentFilter();
492 is_tracing_
= TracingController::GetInstance()->EnableRecording(
493 trace_config
, tracing_enabled_callback_for_testing_
);
494 RecordBackgroundTracingMetric(RECORDING_ENABLED
);
497 void BackgroundTracingManagerImpl::OnFinalizeStarted(
498 base::RefCountedString
* file_contents
) {
499 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
501 RecordBackgroundTracingMetric(FINALIZATION_STARTED
);
502 UMA_HISTOGRAM_MEMORY_KB("Tracing.Background.FinalizingTraceSizeInKB",
503 file_contents
->size() / 1024);
505 if (!receive_callback_
.is_null()) {
506 receive_callback_
.Run(
507 file_contents
, GenerateMetadataDict(),
508 base::Bind(&BackgroundTracingManagerImpl::OnFinalizeComplete
,
509 base::Unretained(this)));
513 void BackgroundTracingManagerImpl::OnFinalizeComplete() {
514 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
515 BrowserThread::PostTask(
516 BrowserThread::UI
, FROM_HERE
,
517 base::Bind(&BackgroundTracingManagerImpl::OnFinalizeComplete
,
518 base::Unretained(this)));
522 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
524 is_gathering_
= false;
526 if (!idle_callback_
.is_null())
527 idle_callback_
.Run();
529 // Now that a trace has completed, we may need to enable recording again.
530 // TODO(oysteine): Retry later if IsAllowedToBeginBackgroundScenario fails.
532 delegate_
->IsAllowedToBeginBackgroundScenario(
533 *config_
.get(), requires_anonymized_data_
)) {
534 EnableRecordingIfConfigNeedsIt();
536 // Clear all the callbacks so we don't keep hearing about histogram changes,
537 // etc. anymore, both in this process and in any child processes.
538 SetupUMACallbacks(CLEAR_CALLBACKS
);
541 RecordBackgroundTracingMetric(FINALIZATION_COMPLETE
);
544 scoped_ptr
<base::DictionaryValue
>
545 BackgroundTracingManagerImpl::GenerateMetadataDict() const {
546 // Grab the network type.
547 std::string network_type
= GetNetworkTypeString();
549 // Grab the product version.
550 std::string product_version
= GetContentClient()->GetProduct();
552 // Serialize the config into json.
553 scoped_ptr
<base::DictionaryValue
> config_dict(new base::DictionaryValue());
555 BackgroundTracingConfig::IntoDict(config_
.get(), config_dict
.get());
557 scoped_ptr
<base::DictionaryValue
> metadata_dict(new base::DictionaryValue());
558 metadata_dict
->Set(kMetaDataConfigKey
, config_dict
.Pass());
559 metadata_dict
->SetString(kMetaDataNetworkType
, network_type
);
560 metadata_dict
->SetString(kMetaDataVersionKey
, product_version
);
562 return metadata_dict
.Pass();
565 void BackgroundTracingManagerImpl::BeginFinalizing(
566 StartedFinalizingCallback callback
) {
567 is_gathering_
= true;
570 bool is_allowed_finalization
=
571 !delegate_
|| (config_
&&
572 delegate_
->IsAllowedToEndBackgroundScenario(
573 *config_
.get(), requires_anonymized_data_
));
575 scoped_refptr
<TracingControllerImpl::TraceDataSink
> trace_data_sink
;
576 if (is_allowed_finalization
) {
577 trace_data_sink
= content::TracingController::CreateCompressedStringSink(
578 content::TracingController::CreateCallbackEndpoint(
579 base::Bind(&BackgroundTracingManagerImpl::OnFinalizeStarted
,
580 base::Unretained(this))));
581 RecordBackgroundTracingMetric(FINALIZATION_ALLOWED
);
583 if (auto metadata_dict
= GenerateMetadataDict()) {
585 if (base::JSONWriter::Write(*metadata_dict
.get(), &results
))
586 trace_data_sink
->SetMetadata(results
);
589 RecordBackgroundTracingMetric(FINALIZATION_DISALLOWED
);
592 content::TracingController::GetInstance()->DisableRecording(trace_data_sink
);
594 if (!callback
.is_null())
595 callback
.Run(is_allowed_finalization
);
598 void BackgroundTracingManagerImpl::AbortScenario() {
599 SetupUMACallbacks(CLEAR_CALLBACKS
);
604 content::TracingController::GetInstance()->DisableRecording(nullptr);
608 BackgroundTracingManagerImpl::GetCategoryFilterStringForCategoryPreset(
609 BackgroundTracingConfig::CategoryPreset preset
) const {
611 case BackgroundTracingConfig::CategoryPreset::BENCHMARK
:
612 return "benchmark,toplevel";
613 case BackgroundTracingConfig::CategoryPreset::BENCHMARK_DEEP
:
614 return "*,disabled-by-default-benchmark.detailed";
620 } // namspace content