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/trace_event.h"
17 namespace trace_event
{
21 // String options that can be used to initialize TraceOptions.
22 const char kRecordUntilFull
[] = "record-until-full";
23 const char kRecordContinuously
[] = "record-continuously";
24 const char kRecordAsMuchAsPossible
[] = "record-as-much-as-possible";
25 const char kTraceToConsole
[] = "trace-to-console";
26 const char kEnableSampling
[] = "enable-sampling";
27 const char kEnableSystrace
[] = "enable-systrace";
28 const char kEnableArgumentFilter
[] = "enable-argument-filter";
30 // String parameters that can be used to parse the trace config string.
31 const char kRecordModeParam
[] = "record_mode";
32 const char kEnableSamplingParam
[] = "enable_sampling";
33 const char kEnableSystraceParam
[] = "enable_systrace";
34 const char kEnableArgumentFilterParam
[] = "enable_argument_filter";
35 const char kIncludedCategoriesParam
[] = "included_categories";
36 const char kExcludedCategoriesParam
[] = "excluded_categories";
37 const char kSyntheticDelaysParam
[] = "synthetic_delays";
39 const char kSyntheticDelayCategoryFilterPrefix
[] = "DELAY(";
41 // String parameters that is used to parse memory dump config in trace config
43 const char kMemoryDumpConfigParam
[] = "memory_dump_config";
44 const char kTriggersParam
[] = "triggers";
45 const char kPeriodicIntervalParam
[] = "periodic_interval_ms";
46 const char kModeParam
[] = "mode";
47 const char kDetailedParam
[] = "detailed";
48 const char kLightParam
[] = "light";
50 // Default configuration of memory dumps.
51 const TraceConfig::MemoryDumpTriggerConfig kDefaultHeavyMemoryDumpTrigger
= {
52 2000, // periodic_interval_ms
53 MemoryDumpArgs::LevelOfDetail::HIGH
};
54 const TraceConfig::MemoryDumpTriggerConfig kDefaultLightMemoryDumpTrigger
= {
55 250, // periodic_interval_ms
56 MemoryDumpArgs::LevelOfDetail::LOW
};
60 TraceConfig::TraceConfig() {
64 TraceConfig::TraceConfig(const std::string
& category_filter_string
,
65 const std::string
& trace_options_string
) {
66 InitializeFromStrings(category_filter_string
, trace_options_string
);
69 TraceConfig::TraceConfig(const std::string
& category_filter_string
,
70 TraceRecordMode record_mode
) {
71 std::string trace_options_string
;
72 switch (record_mode
) {
73 case RECORD_UNTIL_FULL
:
74 trace_options_string
= kRecordUntilFull
;
76 case RECORD_CONTINUOUSLY
:
77 trace_options_string
= kRecordContinuously
;
79 case RECORD_AS_MUCH_AS_POSSIBLE
:
80 trace_options_string
= kRecordAsMuchAsPossible
;
83 trace_options_string
= kTraceToConsole
;
88 InitializeFromStrings(category_filter_string
, trace_options_string
);
91 TraceConfig::TraceConfig(const std::string
& config_string
) {
92 if (!config_string
.empty())
93 InitializeFromConfigString(config_string
);
98 TraceConfig::TraceConfig(const TraceConfig
& tc
)
99 : record_mode_(tc
.record_mode_
),
100 enable_sampling_(tc
.enable_sampling_
),
101 enable_systrace_(tc
.enable_systrace_
),
102 enable_argument_filter_(tc
.enable_argument_filter_
),
103 memory_dump_config_(tc
.memory_dump_config_
),
104 included_categories_(tc
.included_categories_
),
105 disabled_categories_(tc
.disabled_categories_
),
106 excluded_categories_(tc
.excluded_categories_
),
107 synthetic_delays_(tc
.synthetic_delays_
) {}
109 TraceConfig::~TraceConfig() {
112 TraceConfig
& TraceConfig::operator=(const TraceConfig
& rhs
) {
116 record_mode_
= rhs
.record_mode_
;
117 enable_sampling_
= rhs
.enable_sampling_
;
118 enable_systrace_
= rhs
.enable_systrace_
;
119 enable_argument_filter_
= rhs
.enable_argument_filter_
;
120 memory_dump_config_
= rhs
.memory_dump_config_
;
121 included_categories_
= rhs
.included_categories_
;
122 disabled_categories_
= rhs
.disabled_categories_
;
123 excluded_categories_
= rhs
.excluded_categories_
;
124 synthetic_delays_
= rhs
.synthetic_delays_
;
128 const TraceConfig::StringList
& TraceConfig::GetSyntheticDelayValues() const {
129 return synthetic_delays_
;
132 std::string
TraceConfig::ToString() const {
133 base::DictionaryValue dict
;
137 base::JSONWriter::Write(dict
, &json
);
142 std::string
TraceConfig::ToCategoryFilterString() const {
143 std::string filter_string
;
144 WriteCategoryFilterString(included_categories_
, &filter_string
, true);
145 WriteCategoryFilterString(disabled_categories_
, &filter_string
, true);
146 WriteCategoryFilterString(excluded_categories_
, &filter_string
, false);
147 WriteCategoryFilterString(synthetic_delays_
, &filter_string
);
148 return filter_string
;
151 bool TraceConfig::IsCategoryGroupEnabled(
152 const char* category_group_name
) const {
153 // TraceLog should call this method only as part of enabling/disabling
156 bool had_enabled_by_default
= false;
157 DCHECK(category_group_name
);
158 CStringTokenizer
category_group_tokens(
159 category_group_name
, category_group_name
+ strlen(category_group_name
),
161 while (category_group_tokens
.GetNext()) {
162 std::string category_group_token
= category_group_tokens
.token();
163 // Don't allow empty tokens, nor tokens with leading or trailing space.
164 DCHECK(!TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
165 category_group_token
))
166 << "Disallowed category string";
167 if (IsCategoryEnabled(category_group_token
.c_str())) {
170 if (!base::MatchPattern(category_group_token
.c_str(),
171 TRACE_DISABLED_BY_DEFAULT("*")))
172 had_enabled_by_default
= true;
174 // Do a second pass to check for explicitly disabled categories
175 // (those explicitly enabled have priority due to first pass).
176 category_group_tokens
.Reset();
177 bool category_group_disabled
= false;
178 while (category_group_tokens
.GetNext()) {
179 std::string category_group_token
= category_group_tokens
.token();
180 for (StringList::const_iterator ci
= excluded_categories_
.begin();
181 ci
!= excluded_categories_
.end();
183 if (base::MatchPattern(category_group_token
.c_str(), ci
->c_str())) {
184 // Current token of category_group_name is present in excluded_list.
185 // Flag the exclusion and proceed further to check if any of the
186 // remaining categories of category_group_name is not present in the
188 category_group_disabled
= true;
191 // One of the category of category_group_name is not present in
192 // excluded_ list. So, it has to be included_ list. Enable the
193 // category_group_name for recording.
194 category_group_disabled
= false;
196 // One of the categories present in category_group_name is not present in
197 // excluded_ list. Implies this category_group_name group can be enabled
198 // for recording, since one of its groups is enabled for recording.
199 if (!category_group_disabled
)
202 // If the category group is not excluded, and there are no included patterns
203 // we consider this category group enabled, as long as it had categories
204 // other than disabled-by-default.
205 return !category_group_disabled
&&
206 included_categories_
.empty() && had_enabled_by_default
;
209 void TraceConfig::Merge(const TraceConfig
& config
) {
210 if (record_mode_
!= config
.record_mode_
211 || enable_sampling_
!= config
.enable_sampling_
212 || enable_systrace_
!= config
.enable_systrace_
213 || enable_argument_filter_
!= config
.enable_argument_filter_
) {
214 DLOG(ERROR
) << "Attempting to merge trace config with a different "
215 << "set of options.";
218 // Keep included patterns only if both filters have an included entry.
219 // Otherwise, one of the filter was specifying "*" and we want to honor the
221 if (HasIncludedPatterns() && config
.HasIncludedPatterns()) {
222 included_categories_
.insert(included_categories_
.end(),
223 config
.included_categories_
.begin(),
224 config
.included_categories_
.end());
226 included_categories_
.clear();
229 memory_dump_config_
.insert(memory_dump_config_
.end(),
230 config
.memory_dump_config_
.begin(),
231 config
.memory_dump_config_
.end());
233 disabled_categories_
.insert(disabled_categories_
.end(),
234 config
.disabled_categories_
.begin(),
235 config
.disabled_categories_
.end());
236 excluded_categories_
.insert(excluded_categories_
.end(),
237 config
.excluded_categories_
.begin(),
238 config
.excluded_categories_
.end());
239 synthetic_delays_
.insert(synthetic_delays_
.end(),
240 config
.synthetic_delays_
.begin(),
241 config
.synthetic_delays_
.end());
244 void TraceConfig::Clear() {
245 record_mode_
= RECORD_UNTIL_FULL
;
246 enable_sampling_
= false;
247 enable_systrace_
= false;
248 enable_argument_filter_
= false;
249 included_categories_
.clear();
250 disabled_categories_
.clear();
251 excluded_categories_
.clear();
252 synthetic_delays_
.clear();
253 memory_dump_config_
.clear();
256 void TraceConfig::InitializeDefault() {
257 record_mode_
= RECORD_UNTIL_FULL
;
258 enable_sampling_
= false;
259 enable_systrace_
= false;
260 enable_argument_filter_
= false;
261 excluded_categories_
.push_back("*Debug");
262 excluded_categories_
.push_back("*Test");
265 void TraceConfig::InitializeFromConfigString(const std::string
& config_string
) {
266 scoped_ptr
<base::Value
> value(base::JSONReader::Read(config_string
));
267 if (!value
|| !value
->IsType(base::Value::TYPE_DICTIONARY
)) {
271 scoped_ptr
<base::DictionaryValue
> dict(
272 static_cast<base::DictionaryValue
*>(value
.release()));
274 record_mode_
= RECORD_UNTIL_FULL
;
275 std::string record_mode
;
276 if (dict
->GetString(kRecordModeParam
, &record_mode
)) {
277 if (record_mode
== kRecordUntilFull
) {
278 record_mode_
= RECORD_UNTIL_FULL
;
279 } else if (record_mode
== kRecordContinuously
) {
280 record_mode_
= RECORD_CONTINUOUSLY
;
281 } else if (record_mode
== kTraceToConsole
) {
282 record_mode_
= ECHO_TO_CONSOLE
;
283 } else if (record_mode
== kRecordAsMuchAsPossible
) {
284 record_mode_
= RECORD_AS_MUCH_AS_POSSIBLE
;
288 bool enable_sampling
;
289 if (!dict
->GetBoolean(kEnableSamplingParam
, &enable_sampling
))
290 enable_sampling_
= false;
292 enable_sampling_
= enable_sampling
;
294 bool enable_systrace
;
295 if (!dict
->GetBoolean(kEnableSystraceParam
, &enable_systrace
))
296 enable_systrace_
= false;
298 enable_systrace_
= enable_systrace
;
300 bool enable_argument_filter
;
301 if (!dict
->GetBoolean(kEnableArgumentFilterParam
, &enable_argument_filter
))
302 enable_argument_filter_
= false;
304 enable_argument_filter_
= enable_argument_filter
;
306 base::ListValue
* category_list
= nullptr;
307 if (dict
->GetList(kIncludedCategoriesParam
, &category_list
))
308 SetCategoriesFromIncludedList(*category_list
);
309 if (dict
->GetList(kExcludedCategoriesParam
, &category_list
))
310 SetCategoriesFromExcludedList(*category_list
);
311 if (dict
->GetList(kSyntheticDelaysParam
, &category_list
))
312 SetSyntheticDelaysFromList(*category_list
);
314 if (IsCategoryEnabled(MemoryDumpManager::kTraceCategory
)) {
315 // If dump triggers not set, the client is using the legacy with just
316 // category enabled. So, use the default periodic dump config.
317 base::DictionaryValue
* memory_dump_config
= nullptr;
318 if (dict
->GetDictionary(kMemoryDumpConfigParam
, &memory_dump_config
))
319 SetMemoryDumpConfig(*memory_dump_config
);
321 SetDefaultMemoryDumpConfig();
325 void TraceConfig::InitializeFromStrings(
326 const std::string
& category_filter_string
,
327 const std::string
& trace_options_string
) {
328 if (!category_filter_string
.empty()) {
329 std::vector
<std::string
> split
= base::SplitString(
330 category_filter_string
, ",", base::TRIM_WHITESPACE
,
331 base::SPLIT_WANT_ALL
);
332 std::vector
<std::string
>::iterator iter
;
333 for (iter
= split
.begin(); iter
!= split
.end(); ++iter
) {
334 std::string category
= *iter
;
335 // Ignore empty categories.
336 if (category
.empty())
338 // Synthetic delays are of the form 'DELAY(delay;option;option;...)'.
339 if (category
.find(kSyntheticDelayCategoryFilterPrefix
) == 0 &&
340 category
.at(category
.size() - 1) == ')') {
341 category
= category
.substr(
342 strlen(kSyntheticDelayCategoryFilterPrefix
),
343 category
.size() - strlen(kSyntheticDelayCategoryFilterPrefix
) - 1);
344 size_t name_length
= category
.find(';');
345 if (name_length
!= std::string::npos
&& name_length
> 0 &&
346 name_length
!= category
.size() - 1) {
347 synthetic_delays_
.push_back(category
);
349 } else if (category
.at(0) == '-') {
350 // Excluded categories start with '-'.
351 // Remove '-' from category string.
352 category
= category
.substr(1);
353 excluded_categories_
.push_back(category
);
354 } else if (category
.compare(0, strlen(TRACE_DISABLED_BY_DEFAULT("")),
355 TRACE_DISABLED_BY_DEFAULT("")) == 0) {
356 disabled_categories_
.push_back(category
);
358 included_categories_
.push_back(category
);
363 record_mode_
= RECORD_UNTIL_FULL
;
364 enable_sampling_
= false;
365 enable_systrace_
= false;
366 enable_argument_filter_
= false;
367 if(!trace_options_string
.empty()) {
368 std::vector
<std::string
> split
= base::SplitString(
369 trace_options_string
, ",", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
370 std::vector
<std::string
>::iterator iter
;
371 for (iter
= split
.begin(); iter
!= split
.end(); ++iter
) {
372 if (*iter
== kRecordUntilFull
) {
373 record_mode_
= RECORD_UNTIL_FULL
;
374 } else if (*iter
== kRecordContinuously
) {
375 record_mode_
= RECORD_CONTINUOUSLY
;
376 } else if (*iter
== kTraceToConsole
) {
377 record_mode_
= ECHO_TO_CONSOLE
;
378 } else if (*iter
== kRecordAsMuchAsPossible
) {
379 record_mode_
= RECORD_AS_MUCH_AS_POSSIBLE
;
380 } else if (*iter
== kEnableSampling
) {
381 enable_sampling_
= true;
382 } else if (*iter
== kEnableSystrace
) {
383 enable_systrace_
= true;
384 } else if (*iter
== kEnableArgumentFilter
) {
385 enable_argument_filter_
= true;
390 if (IsCategoryEnabled(MemoryDumpManager::kTraceCategory
)) {
391 SetDefaultMemoryDumpConfig();
395 void TraceConfig::SetCategoriesFromIncludedList(
396 const base::ListValue
& included_list
) {
397 included_categories_
.clear();
398 for (size_t i
= 0; i
< included_list
.GetSize(); ++i
) {
399 std::string category
;
400 if (!included_list
.GetString(i
, &category
))
402 if (category
.compare(0, strlen(TRACE_DISABLED_BY_DEFAULT("")),
403 TRACE_DISABLED_BY_DEFAULT("")) == 0) {
404 disabled_categories_
.push_back(category
);
406 included_categories_
.push_back(category
);
411 void TraceConfig::SetCategoriesFromExcludedList(
412 const base::ListValue
& excluded_list
) {
413 excluded_categories_
.clear();
414 for (size_t i
= 0; i
< excluded_list
.GetSize(); ++i
) {
415 std::string category
;
416 if (excluded_list
.GetString(i
, &category
))
417 excluded_categories_
.push_back(category
);
421 void TraceConfig::SetSyntheticDelaysFromList(const base::ListValue
& list
) {
422 synthetic_delays_
.clear();
423 for (size_t i
= 0; i
< list
.GetSize(); ++i
) {
425 if (!list
.GetString(i
, &delay
))
427 // Synthetic delays are of the form "delay;option;option;...".
428 size_t name_length
= delay
.find(';');
429 if (name_length
!= std::string::npos
&& name_length
> 0 &&
430 name_length
!= delay
.size() - 1) {
431 synthetic_delays_
.push_back(delay
);
436 void TraceConfig::AddCategoryToDict(base::DictionaryValue
& dict
,
438 const StringList
& categories
) const {
439 if (categories
.empty())
442 scoped_ptr
<base::ListValue
> list(new base::ListValue());
443 for (StringList::const_iterator ci
= categories
.begin();
444 ci
!= categories
.end();
446 list
->AppendString(*ci
);
449 dict
.Set(param
, list
.Pass());
452 void TraceConfig::SetMemoryDumpConfig(
453 const base::DictionaryValue
& memory_dump_config
) {
454 memory_dump_config_
.clear();
456 const base::ListValue
* trigger_list
= nullptr;
457 if (!memory_dump_config
.GetList(kTriggersParam
, &trigger_list
) ||
458 trigger_list
->GetSize() == 0) {
462 for (size_t i
= 0; i
< trigger_list
->GetSize(); ++i
) {
463 const base::DictionaryValue
* trigger
= nullptr;
464 if (!trigger_list
->GetDictionary(i
, &trigger
))
467 MemoryDumpTriggerConfig dump_config
;
468 std::string dump_type
;
471 if (!trigger
->GetInteger(kPeriodicIntervalParam
, &interval
)) {
474 DCHECK_GT(interval
, 0);
475 dump_config
.periodic_interval_ms
= static_cast<uint32
>(interval
);
476 dump_config
.level_of_detail
= MemoryDumpArgs::LevelOfDetail::LOW
;
478 if (trigger
->GetString(kModeParam
, &dump_type
)) {
479 if (dump_type
== kDetailedParam
) {
480 dump_config
.level_of_detail
= MemoryDumpArgs::LevelOfDetail::HIGH
;
484 memory_dump_config_
.push_back(dump_config
);
488 void TraceConfig::SetDefaultMemoryDumpConfig() {
489 memory_dump_config_
.clear();
490 memory_dump_config_
.push_back(kDefaultHeavyMemoryDumpTrigger
);
491 memory_dump_config_
.push_back(kDefaultLightMemoryDumpTrigger
);
494 void TraceConfig::ToDict(base::DictionaryValue
& dict
) const {
495 switch (record_mode_
) {
496 case RECORD_UNTIL_FULL
:
497 dict
.SetString(kRecordModeParam
, kRecordUntilFull
);
499 case RECORD_CONTINUOUSLY
:
500 dict
.SetString(kRecordModeParam
, kRecordContinuously
);
502 case RECORD_AS_MUCH_AS_POSSIBLE
:
503 dict
.SetString(kRecordModeParam
, kRecordAsMuchAsPossible
);
505 case ECHO_TO_CONSOLE
:
506 dict
.SetString(kRecordModeParam
, kTraceToConsole
);
512 if (enable_sampling_
)
513 dict
.SetBoolean(kEnableSamplingParam
, true);
515 dict
.SetBoolean(kEnableSamplingParam
, false);
517 if (enable_systrace_
)
518 dict
.SetBoolean(kEnableSystraceParam
, true);
520 dict
.SetBoolean(kEnableSystraceParam
, false);
522 if (enable_argument_filter_
)
523 dict
.SetBoolean(kEnableArgumentFilterParam
, true);
525 dict
.SetBoolean(kEnableArgumentFilterParam
, false);
527 StringList
categories(included_categories_
);
528 categories
.insert(categories
.end(),
529 disabled_categories_
.begin(),
530 disabled_categories_
.end());
531 AddCategoryToDict(dict
, kIncludedCategoriesParam
, categories
);
532 AddCategoryToDict(dict
, kExcludedCategoriesParam
, excluded_categories_
);
533 AddCategoryToDict(dict
, kSyntheticDelaysParam
, synthetic_delays_
);
535 if (IsCategoryEnabled(MemoryDumpManager::kTraceCategory
)) {
536 scoped_ptr
<base::DictionaryValue
> memory_dump_config(
537 new base::DictionaryValue());
538 scoped_ptr
<base::ListValue
> triggers_list(new base::ListValue());
539 for (const MemoryDumpTriggerConfig
& config
: memory_dump_config_
) {
540 scoped_ptr
<base::DictionaryValue
> trigger_dict(
541 new base::DictionaryValue());
542 trigger_dict
->SetInteger(kPeriodicIntervalParam
,
543 static_cast<int>(config
.periodic_interval_ms
));
545 switch (config
.level_of_detail
) {
546 case MemoryDumpArgs::LevelOfDetail::LOW
:
547 trigger_dict
->SetString(kModeParam
, kLightParam
);
549 case MemoryDumpArgs::LevelOfDetail::HIGH
:
550 trigger_dict
->SetString(kModeParam
, kDetailedParam
);
555 triggers_list
->Append(trigger_dict
.Pass());
558 // Empty triggers will still be specified explicitly since it means that
559 // the periodic dumps are not enabled.
560 memory_dump_config
->Set(kTriggersParam
, triggers_list
.Pass());
561 dict
.Set(kMemoryDumpConfigParam
, memory_dump_config
.Pass());
565 std::string
TraceConfig::ToTraceOptionsString() const {
567 switch (record_mode_
) {
568 case RECORD_UNTIL_FULL
:
569 ret
= kRecordUntilFull
;
571 case RECORD_CONTINUOUSLY
:
572 ret
= kRecordContinuously
;
574 case RECORD_AS_MUCH_AS_POSSIBLE
:
575 ret
= kRecordAsMuchAsPossible
;
577 case ECHO_TO_CONSOLE
:
578 ret
= kTraceToConsole
;
583 if (enable_sampling_
)
584 ret
= ret
+ "," + kEnableSampling
;
585 if (enable_systrace_
)
586 ret
= ret
+ "," + kEnableSystrace
;
587 if (enable_argument_filter_
)
588 ret
= ret
+ "," + kEnableArgumentFilter
;
592 void TraceConfig::WriteCategoryFilterString(const StringList
& values
,
594 bool included
) const {
595 bool prepend_comma
= !out
->empty();
597 for (StringList::const_iterator ci
= values
.begin();
598 ci
!= values
.end(); ++ci
) {
599 if (token_cnt
> 0 || prepend_comma
)
600 StringAppendF(out
, ",");
601 StringAppendF(out
, "%s%s", (included
? "" : "-"), ci
->c_str());
606 void TraceConfig::WriteCategoryFilterString(const StringList
& delays
,
607 std::string
* out
) const {
608 bool prepend_comma
= !out
->empty();
610 for (StringList::const_iterator ci
= delays
.begin();
611 ci
!= delays
.end(); ++ci
) {
612 if (token_cnt
> 0 || prepend_comma
)
613 StringAppendF(out
, ",");
614 StringAppendF(out
, "%s%s)", kSyntheticDelayCategoryFilterPrefix
,
620 bool TraceConfig::IsCategoryEnabled(const char* category_name
) const {
621 StringList::const_iterator ci
;
623 // Check the disabled- filters and the disabled-* wildcard first so that a
624 // "*" filter does not include the disabled.
625 for (ci
= disabled_categories_
.begin();
626 ci
!= disabled_categories_
.end();
628 if (base::MatchPattern(category_name
, ci
->c_str()))
632 if (base::MatchPattern(category_name
, TRACE_DISABLED_BY_DEFAULT("*")))
635 for (ci
= included_categories_
.begin();
636 ci
!= included_categories_
.end();
638 if (base::MatchPattern(category_name
, ci
->c_str()))
645 bool TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
646 const std::string
& str
) {
647 return str
.empty() ||
649 str
.at(str
.length() - 1) == ' ';
652 bool TraceConfig::HasIncludedPatterns() const {
653 return !included_categories_
.empty();
656 } // namespace trace_event