1 // Copyright (c) 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 "base/trace_event/trace_config.h"
7 #include "base/json/json_reader.h"
8 #include "base/json/json_writer.h"
9 #include "base/strings/pattern.h"
10 #include "base/strings/string_split.h"
11 #include "base/strings/string_tokenizer.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/trace_event/memory_dump_manager.h"
14 #include "base/trace_event/memory_dump_request_args.h"
15 #include "base/trace_event/trace_event.h"
18 namespace trace_event
{
22 // String options that can be used to initialize TraceOptions.
23 const char kRecordUntilFull
[] = "record-until-full";
24 const char kRecordContinuously
[] = "record-continuously";
25 const char kRecordAsMuchAsPossible
[] = "record-as-much-as-possible";
26 const char kTraceToConsole
[] = "trace-to-console";
27 const char kEnableSampling
[] = "enable-sampling";
28 const char kEnableSystrace
[] = "enable-systrace";
29 const char kEnableArgumentFilter
[] = "enable-argument-filter";
31 // String parameters that can be used to parse the trace config string.
32 const char kRecordModeParam
[] = "record_mode";
33 const char kEnableSamplingParam
[] = "enable_sampling";
34 const char kEnableSystraceParam
[] = "enable_systrace";
35 const char kEnableArgumentFilterParam
[] = "enable_argument_filter";
36 const char kIncludedCategoriesParam
[] = "included_categories";
37 const char kExcludedCategoriesParam
[] = "excluded_categories";
38 const char kSyntheticDelaysParam
[] = "synthetic_delays";
40 const char kSyntheticDelayCategoryFilterPrefix
[] = "DELAY(";
42 // String parameters that is used to parse memory dump config in trace config
44 const char kMemoryDumpConfigParam
[] = "memory_dump_config";
45 const char kTriggersParam
[] = "triggers";
46 const char kPeriodicIntervalParam
[] = "periodic_interval_ms";
47 const char kModeParam
[] = "mode";
49 // Default configuration of memory dumps.
50 const TraceConfig::MemoryDumpTriggerConfig kDefaultHeavyMemoryDumpTrigger
= {
51 2000, // periodic_interval_ms
52 MemoryDumpLevelOfDetail::DETAILED
};
53 const TraceConfig::MemoryDumpTriggerConfig kDefaultLightMemoryDumpTrigger
= {
54 250, // periodic_interval_ms
55 MemoryDumpLevelOfDetail::LIGHT
};
59 TraceConfig::TraceConfig() {
63 TraceConfig::TraceConfig(const std::string
& category_filter_string
,
64 const std::string
& trace_options_string
) {
65 InitializeFromStrings(category_filter_string
, trace_options_string
);
68 TraceConfig::TraceConfig(const std::string
& category_filter_string
,
69 TraceRecordMode record_mode
) {
70 std::string trace_options_string
;
71 switch (record_mode
) {
72 case RECORD_UNTIL_FULL
:
73 trace_options_string
= kRecordUntilFull
;
75 case RECORD_CONTINUOUSLY
:
76 trace_options_string
= kRecordContinuously
;
78 case RECORD_AS_MUCH_AS_POSSIBLE
:
79 trace_options_string
= kRecordAsMuchAsPossible
;
82 trace_options_string
= kTraceToConsole
;
87 InitializeFromStrings(category_filter_string
, trace_options_string
);
90 TraceConfig::TraceConfig(const std::string
& config_string
) {
91 if (!config_string
.empty())
92 InitializeFromConfigString(config_string
);
97 TraceConfig::TraceConfig(const TraceConfig
& tc
)
98 : record_mode_(tc
.record_mode_
),
99 enable_sampling_(tc
.enable_sampling_
),
100 enable_systrace_(tc
.enable_systrace_
),
101 enable_argument_filter_(tc
.enable_argument_filter_
),
102 memory_dump_config_(tc
.memory_dump_config_
),
103 included_categories_(tc
.included_categories_
),
104 disabled_categories_(tc
.disabled_categories_
),
105 excluded_categories_(tc
.excluded_categories_
),
106 synthetic_delays_(tc
.synthetic_delays_
) {}
108 TraceConfig::~TraceConfig() {
111 TraceConfig
& TraceConfig::operator=(const TraceConfig
& rhs
) {
115 record_mode_
= rhs
.record_mode_
;
116 enable_sampling_
= rhs
.enable_sampling_
;
117 enable_systrace_
= rhs
.enable_systrace_
;
118 enable_argument_filter_
= rhs
.enable_argument_filter_
;
119 memory_dump_config_
= rhs
.memory_dump_config_
;
120 included_categories_
= rhs
.included_categories_
;
121 disabled_categories_
= rhs
.disabled_categories_
;
122 excluded_categories_
= rhs
.excluded_categories_
;
123 synthetic_delays_
= rhs
.synthetic_delays_
;
127 const TraceConfig::StringList
& TraceConfig::GetSyntheticDelayValues() const {
128 return synthetic_delays_
;
131 std::string
TraceConfig::ToString() const {
132 base::DictionaryValue dict
;
136 base::JSONWriter::Write(dict
, &json
);
141 std::string
TraceConfig::ToCategoryFilterString() const {
142 std::string filter_string
;
143 WriteCategoryFilterString(included_categories_
, &filter_string
, true);
144 WriteCategoryFilterString(disabled_categories_
, &filter_string
, true);
145 WriteCategoryFilterString(excluded_categories_
, &filter_string
, false);
146 WriteCategoryFilterString(synthetic_delays_
, &filter_string
);
147 return filter_string
;
150 bool TraceConfig::IsCategoryGroupEnabled(
151 const char* category_group_name
) const {
152 // TraceLog should call this method only as part of enabling/disabling
155 bool had_enabled_by_default
= false;
156 DCHECK(category_group_name
);
157 CStringTokenizer
category_group_tokens(
158 category_group_name
, category_group_name
+ strlen(category_group_name
),
160 while (category_group_tokens
.GetNext()) {
161 std::string category_group_token
= category_group_tokens
.token();
162 // Don't allow empty tokens, nor tokens with leading or trailing space.
163 DCHECK(!TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
164 category_group_token
))
165 << "Disallowed category string";
166 if (IsCategoryEnabled(category_group_token
.c_str())) {
169 if (!base::MatchPattern(category_group_token
.c_str(),
170 TRACE_DISABLED_BY_DEFAULT("*")))
171 had_enabled_by_default
= true;
173 // Do a second pass to check for explicitly disabled categories
174 // (those explicitly enabled have priority due to first pass).
175 category_group_tokens
.Reset();
176 bool category_group_disabled
= false;
177 while (category_group_tokens
.GetNext()) {
178 std::string category_group_token
= category_group_tokens
.token();
179 for (StringList::const_iterator ci
= excluded_categories_
.begin();
180 ci
!= excluded_categories_
.end();
182 if (base::MatchPattern(category_group_token
.c_str(), ci
->c_str())) {
183 // Current token of category_group_name is present in excluded_list.
184 // Flag the exclusion and proceed further to check if any of the
185 // remaining categories of category_group_name is not present in the
187 category_group_disabled
= true;
190 // One of the category of category_group_name is not present in
191 // excluded_ list. So, it has to be included_ list. Enable the
192 // category_group_name for recording.
193 category_group_disabled
= false;
195 // One of the categories present in category_group_name is not present in
196 // excluded_ list. Implies this category_group_name group can be enabled
197 // for recording, since one of its groups is enabled for recording.
198 if (!category_group_disabled
)
201 // If the category group is not excluded, and there are no included patterns
202 // we consider this category group enabled, as long as it had categories
203 // other than disabled-by-default.
204 return !category_group_disabled
&&
205 included_categories_
.empty() && had_enabled_by_default
;
208 void TraceConfig::Merge(const TraceConfig
& config
) {
209 if (record_mode_
!= config
.record_mode_
210 || enable_sampling_
!= config
.enable_sampling_
211 || enable_systrace_
!= config
.enable_systrace_
212 || enable_argument_filter_
!= config
.enable_argument_filter_
) {
213 DLOG(ERROR
) << "Attempting to merge trace config with a different "
214 << "set of options.";
217 // Keep included patterns only if both filters have an included entry.
218 // Otherwise, one of the filter was specifying "*" and we want to honor the
220 if (HasIncludedPatterns() && config
.HasIncludedPatterns()) {
221 included_categories_
.insert(included_categories_
.end(),
222 config
.included_categories_
.begin(),
223 config
.included_categories_
.end());
225 included_categories_
.clear();
228 memory_dump_config_
.insert(memory_dump_config_
.end(),
229 config
.memory_dump_config_
.begin(),
230 config
.memory_dump_config_
.end());
232 disabled_categories_
.insert(disabled_categories_
.end(),
233 config
.disabled_categories_
.begin(),
234 config
.disabled_categories_
.end());
235 excluded_categories_
.insert(excluded_categories_
.end(),
236 config
.excluded_categories_
.begin(),
237 config
.excluded_categories_
.end());
238 synthetic_delays_
.insert(synthetic_delays_
.end(),
239 config
.synthetic_delays_
.begin(),
240 config
.synthetic_delays_
.end());
243 void TraceConfig::Clear() {
244 record_mode_
= RECORD_UNTIL_FULL
;
245 enable_sampling_
= false;
246 enable_systrace_
= false;
247 enable_argument_filter_
= false;
248 included_categories_
.clear();
249 disabled_categories_
.clear();
250 excluded_categories_
.clear();
251 synthetic_delays_
.clear();
252 memory_dump_config_
.clear();
255 void TraceConfig::InitializeDefault() {
256 record_mode_
= RECORD_UNTIL_FULL
;
257 enable_sampling_
= false;
258 enable_systrace_
= false;
259 enable_argument_filter_
= false;
260 excluded_categories_
.push_back("*Debug");
261 excluded_categories_
.push_back("*Test");
264 void TraceConfig::InitializeFromConfigString(const std::string
& config_string
) {
265 scoped_ptr
<base::Value
> value(base::JSONReader::Read(config_string
));
266 if (!value
|| !value
->IsType(base::Value::TYPE_DICTIONARY
)) {
270 scoped_ptr
<base::DictionaryValue
> dict(
271 static_cast<base::DictionaryValue
*>(value
.release()));
273 record_mode_
= RECORD_UNTIL_FULL
;
274 std::string record_mode
;
275 if (dict
->GetString(kRecordModeParam
, &record_mode
)) {
276 if (record_mode
== kRecordUntilFull
) {
277 record_mode_
= RECORD_UNTIL_FULL
;
278 } else if (record_mode
== kRecordContinuously
) {
279 record_mode_
= RECORD_CONTINUOUSLY
;
280 } else if (record_mode
== kTraceToConsole
) {
281 record_mode_
= ECHO_TO_CONSOLE
;
282 } else if (record_mode
== kRecordAsMuchAsPossible
) {
283 record_mode_
= RECORD_AS_MUCH_AS_POSSIBLE
;
287 bool enable_sampling
;
288 if (!dict
->GetBoolean(kEnableSamplingParam
, &enable_sampling
))
289 enable_sampling_
= false;
291 enable_sampling_
= enable_sampling
;
293 bool enable_systrace
;
294 if (!dict
->GetBoolean(kEnableSystraceParam
, &enable_systrace
))
295 enable_systrace_
= false;
297 enable_systrace_
= enable_systrace
;
299 bool enable_argument_filter
;
300 if (!dict
->GetBoolean(kEnableArgumentFilterParam
, &enable_argument_filter
))
301 enable_argument_filter_
= false;
303 enable_argument_filter_
= enable_argument_filter
;
305 base::ListValue
* category_list
= nullptr;
306 if (dict
->GetList(kIncludedCategoriesParam
, &category_list
))
307 SetCategoriesFromIncludedList(*category_list
);
308 if (dict
->GetList(kExcludedCategoriesParam
, &category_list
))
309 SetCategoriesFromExcludedList(*category_list
);
310 if (dict
->GetList(kSyntheticDelaysParam
, &category_list
))
311 SetSyntheticDelaysFromList(*category_list
);
313 if (IsCategoryEnabled(MemoryDumpManager::kTraceCategory
)) {
314 // If dump triggers not set, the client is using the legacy with just
315 // category enabled. So, use the default periodic dump config.
316 base::DictionaryValue
* memory_dump_config
= nullptr;
317 if (dict
->GetDictionary(kMemoryDumpConfigParam
, &memory_dump_config
))
318 SetMemoryDumpConfig(*memory_dump_config
);
320 SetDefaultMemoryDumpConfig();
324 void TraceConfig::InitializeFromStrings(
325 const std::string
& category_filter_string
,
326 const std::string
& trace_options_string
) {
327 if (!category_filter_string
.empty()) {
328 std::vector
<std::string
> split
= base::SplitString(
329 category_filter_string
, ",", base::TRIM_WHITESPACE
,
330 base::SPLIT_WANT_ALL
);
331 std::vector
<std::string
>::iterator iter
;
332 for (iter
= split
.begin(); iter
!= split
.end(); ++iter
) {
333 std::string category
= *iter
;
334 // Ignore empty categories.
335 if (category
.empty())
337 // Synthetic delays are of the form 'DELAY(delay;option;option;...)'.
338 if (category
.find(kSyntheticDelayCategoryFilterPrefix
) == 0 &&
339 category
.at(category
.size() - 1) == ')') {
340 category
= category
.substr(
341 strlen(kSyntheticDelayCategoryFilterPrefix
),
342 category
.size() - strlen(kSyntheticDelayCategoryFilterPrefix
) - 1);
343 size_t name_length
= category
.find(';');
344 if (name_length
!= std::string::npos
&& name_length
> 0 &&
345 name_length
!= category
.size() - 1) {
346 synthetic_delays_
.push_back(category
);
348 } else if (category
.at(0) == '-') {
349 // Excluded categories start with '-'.
350 // Remove '-' from category string.
351 category
= category
.substr(1);
352 excluded_categories_
.push_back(category
);
353 } else if (category
.compare(0, strlen(TRACE_DISABLED_BY_DEFAULT("")),
354 TRACE_DISABLED_BY_DEFAULT("")) == 0) {
355 disabled_categories_
.push_back(category
);
357 included_categories_
.push_back(category
);
362 record_mode_
= RECORD_UNTIL_FULL
;
363 enable_sampling_
= false;
364 enable_systrace_
= false;
365 enable_argument_filter_
= false;
366 if(!trace_options_string
.empty()) {
367 std::vector
<std::string
> split
= base::SplitString(
368 trace_options_string
, ",", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
369 std::vector
<std::string
>::iterator iter
;
370 for (iter
= split
.begin(); iter
!= split
.end(); ++iter
) {
371 if (*iter
== kRecordUntilFull
) {
372 record_mode_
= RECORD_UNTIL_FULL
;
373 } else if (*iter
== kRecordContinuously
) {
374 record_mode_
= RECORD_CONTINUOUSLY
;
375 } else if (*iter
== kTraceToConsole
) {
376 record_mode_
= ECHO_TO_CONSOLE
;
377 } else if (*iter
== kRecordAsMuchAsPossible
) {
378 record_mode_
= RECORD_AS_MUCH_AS_POSSIBLE
;
379 } else if (*iter
== kEnableSampling
) {
380 enable_sampling_
= true;
381 } else if (*iter
== kEnableSystrace
) {
382 enable_systrace_
= true;
383 } else if (*iter
== kEnableArgumentFilter
) {
384 enable_argument_filter_
= true;
389 if (IsCategoryEnabled(MemoryDumpManager::kTraceCategory
)) {
390 SetDefaultMemoryDumpConfig();
394 void TraceConfig::SetCategoriesFromIncludedList(
395 const base::ListValue
& included_list
) {
396 included_categories_
.clear();
397 for (size_t i
= 0; i
< included_list
.GetSize(); ++i
) {
398 std::string category
;
399 if (!included_list
.GetString(i
, &category
))
401 if (category
.compare(0, strlen(TRACE_DISABLED_BY_DEFAULT("")),
402 TRACE_DISABLED_BY_DEFAULT("")) == 0) {
403 disabled_categories_
.push_back(category
);
405 included_categories_
.push_back(category
);
410 void TraceConfig::SetCategoriesFromExcludedList(
411 const base::ListValue
& excluded_list
) {
412 excluded_categories_
.clear();
413 for (size_t i
= 0; i
< excluded_list
.GetSize(); ++i
) {
414 std::string category
;
415 if (excluded_list
.GetString(i
, &category
))
416 excluded_categories_
.push_back(category
);
420 void TraceConfig::SetSyntheticDelaysFromList(const base::ListValue
& list
) {
421 synthetic_delays_
.clear();
422 for (size_t i
= 0; i
< list
.GetSize(); ++i
) {
424 if (!list
.GetString(i
, &delay
))
426 // Synthetic delays are of the form "delay;option;option;...".
427 size_t name_length
= delay
.find(';');
428 if (name_length
!= std::string::npos
&& name_length
> 0 &&
429 name_length
!= delay
.size() - 1) {
430 synthetic_delays_
.push_back(delay
);
435 void TraceConfig::AddCategoryToDict(base::DictionaryValue
& dict
,
437 const StringList
& categories
) const {
438 if (categories
.empty())
441 scoped_ptr
<base::ListValue
> list(new base::ListValue());
442 for (StringList::const_iterator ci
= categories
.begin();
443 ci
!= categories
.end();
445 list
->AppendString(*ci
);
448 dict
.Set(param
, list
.Pass());
451 void TraceConfig::SetMemoryDumpConfig(
452 const base::DictionaryValue
& memory_dump_config
) {
453 memory_dump_config_
.clear();
455 const base::ListValue
* trigger_list
= nullptr;
456 if (!memory_dump_config
.GetList(kTriggersParam
, &trigger_list
) ||
457 trigger_list
->GetSize() == 0) {
461 for (size_t i
= 0; i
< trigger_list
->GetSize(); ++i
) {
462 const base::DictionaryValue
* trigger
= nullptr;
463 if (!trigger_list
->GetDictionary(i
, &trigger
))
466 MemoryDumpTriggerConfig dump_config
;
469 if (!trigger
->GetInteger(kPeriodicIntervalParam
, &interval
)) {
472 DCHECK_GT(interval
, 0);
473 dump_config
.periodic_interval_ms
= static_cast<uint32
>(interval
);
474 std::string level_of_detail_str
;
475 trigger
->GetString(kModeParam
, &level_of_detail_str
);
476 dump_config
.level_of_detail
=
477 StringToMemoryDumpLevelOfDetail(level_of_detail_str
);
478 memory_dump_config_
.push_back(dump_config
);
482 void TraceConfig::SetDefaultMemoryDumpConfig() {
483 memory_dump_config_
.clear();
484 memory_dump_config_
.push_back(kDefaultHeavyMemoryDumpTrigger
);
485 memory_dump_config_
.push_back(kDefaultLightMemoryDumpTrigger
);
488 void TraceConfig::ToDict(base::DictionaryValue
& dict
) const {
489 switch (record_mode_
) {
490 case RECORD_UNTIL_FULL
:
491 dict
.SetString(kRecordModeParam
, kRecordUntilFull
);
493 case RECORD_CONTINUOUSLY
:
494 dict
.SetString(kRecordModeParam
, kRecordContinuously
);
496 case RECORD_AS_MUCH_AS_POSSIBLE
:
497 dict
.SetString(kRecordModeParam
, kRecordAsMuchAsPossible
);
499 case ECHO_TO_CONSOLE
:
500 dict
.SetString(kRecordModeParam
, kTraceToConsole
);
506 if (enable_sampling_
)
507 dict
.SetBoolean(kEnableSamplingParam
, true);
509 dict
.SetBoolean(kEnableSamplingParam
, false);
511 if (enable_systrace_
)
512 dict
.SetBoolean(kEnableSystraceParam
, true);
514 dict
.SetBoolean(kEnableSystraceParam
, false);
516 if (enable_argument_filter_
)
517 dict
.SetBoolean(kEnableArgumentFilterParam
, true);
519 dict
.SetBoolean(kEnableArgumentFilterParam
, false);
521 StringList
categories(included_categories_
);
522 categories
.insert(categories
.end(),
523 disabled_categories_
.begin(),
524 disabled_categories_
.end());
525 AddCategoryToDict(dict
, kIncludedCategoriesParam
, categories
);
526 AddCategoryToDict(dict
, kExcludedCategoriesParam
, excluded_categories_
);
527 AddCategoryToDict(dict
, kSyntheticDelaysParam
, synthetic_delays_
);
529 if (IsCategoryEnabled(MemoryDumpManager::kTraceCategory
)) {
530 scoped_ptr
<base::DictionaryValue
> memory_dump_config(
531 new base::DictionaryValue());
532 scoped_ptr
<base::ListValue
> triggers_list(new base::ListValue());
533 for (const MemoryDumpTriggerConfig
& config
: memory_dump_config_
) {
534 scoped_ptr
<base::DictionaryValue
> trigger_dict(
535 new base::DictionaryValue());
536 trigger_dict
->SetInteger(kPeriodicIntervalParam
,
537 static_cast<int>(config
.periodic_interval_ms
));
538 trigger_dict
->SetString(
539 kModeParam
, MemoryDumpLevelOfDetailToString(config
.level_of_detail
));
540 triggers_list
->Append(trigger_dict
.Pass());
543 // Empty triggers will still be specified explicitly since it means that
544 // the periodic dumps are not enabled.
545 memory_dump_config
->Set(kTriggersParam
, triggers_list
.Pass());
546 dict
.Set(kMemoryDumpConfigParam
, memory_dump_config
.Pass());
550 std::string
TraceConfig::ToTraceOptionsString() const {
552 switch (record_mode_
) {
553 case RECORD_UNTIL_FULL
:
554 ret
= kRecordUntilFull
;
556 case RECORD_CONTINUOUSLY
:
557 ret
= kRecordContinuously
;
559 case RECORD_AS_MUCH_AS_POSSIBLE
:
560 ret
= kRecordAsMuchAsPossible
;
562 case ECHO_TO_CONSOLE
:
563 ret
= kTraceToConsole
;
568 if (enable_sampling_
)
569 ret
= ret
+ "," + kEnableSampling
;
570 if (enable_systrace_
)
571 ret
= ret
+ "," + kEnableSystrace
;
572 if (enable_argument_filter_
)
573 ret
= ret
+ "," + kEnableArgumentFilter
;
577 void TraceConfig::WriteCategoryFilterString(const StringList
& values
,
579 bool included
) const {
580 bool prepend_comma
= !out
->empty();
582 for (StringList::const_iterator ci
= values
.begin();
583 ci
!= values
.end(); ++ci
) {
584 if (token_cnt
> 0 || prepend_comma
)
585 StringAppendF(out
, ",");
586 StringAppendF(out
, "%s%s", (included
? "" : "-"), ci
->c_str());
591 void TraceConfig::WriteCategoryFilterString(const StringList
& delays
,
592 std::string
* out
) const {
593 bool prepend_comma
= !out
->empty();
595 for (StringList::const_iterator ci
= delays
.begin();
596 ci
!= delays
.end(); ++ci
) {
597 if (token_cnt
> 0 || prepend_comma
)
598 StringAppendF(out
, ",");
599 StringAppendF(out
, "%s%s)", kSyntheticDelayCategoryFilterPrefix
,
605 bool TraceConfig::IsCategoryEnabled(const char* category_name
) const {
606 StringList::const_iterator ci
;
608 // Check the disabled- filters and the disabled-* wildcard first so that a
609 // "*" filter does not include the disabled.
610 for (ci
= disabled_categories_
.begin();
611 ci
!= disabled_categories_
.end();
613 if (base::MatchPattern(category_name
, ci
->c_str()))
617 if (base::MatchPattern(category_name
, TRACE_DISABLED_BY_DEFAULT("*")))
620 for (ci
= included_categories_
.begin();
621 ci
!= included_categories_
.end();
623 if (base::MatchPattern(category_name
, ci
->c_str()))
630 bool TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
631 const std::string
& str
) {
632 return str
.empty() ||
634 str
.at(str
.length() - 1) == ' ';
637 bool TraceConfig::HasIncludedPatterns() const {
638 return !included_categories_
.empty();
641 } // namespace trace_event