Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / base / trace_event / trace_config.cc
blobfe822a6635f825f4b24525c5ba97ab53a6d68f5c
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"
16 namespace base {
17 namespace trace_event {
19 namespace {
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
42 // string.
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};
58 } // namespace
60 TraceConfig::TraceConfig() {
61 InitializeDefault();
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;
75 break;
76 case RECORD_CONTINUOUSLY:
77 trace_options_string = kRecordContinuously;
78 break;
79 case RECORD_AS_MUCH_AS_POSSIBLE:
80 trace_options_string = kRecordAsMuchAsPossible;
81 break;
82 case ECHO_TO_CONSOLE:
83 trace_options_string = kTraceToConsole;
84 break;
85 default:
86 NOTREACHED();
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);
94 else
95 InitializeDefault();
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) {
113 if (this == &rhs)
114 return *this;
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_;
125 return *this;
128 const TraceConfig::StringList& TraceConfig::GetSyntheticDelayValues() const {
129 return synthetic_delays_;
132 std::string TraceConfig::ToString() const {
133 base::DictionaryValue dict;
134 ToDict(dict);
136 std::string json;
137 base::JSONWriter::Write(dict, &json);
139 return 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
154 // categories.
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),
160 ",");
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())) {
168 return true;
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();
182 ++ci) {
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
187 // excluded_ list.
188 category_group_disabled = true;
189 break;
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)
200 break;
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
220 // broadest filter.
221 if (HasIncludedPatterns() && config.HasIncludedPatterns()) {
222 included_categories_.insert(included_categories_.end(),
223 config.included_categories_.begin(),
224 config.included_categories_.end());
225 } else {
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)) {
268 InitializeDefault();
269 return;
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;
291 else
292 enable_sampling_ = enable_sampling;
294 bool enable_systrace;
295 if (!dict->GetBoolean(kEnableSystraceParam, &enable_systrace))
296 enable_systrace_ = false;
297 else
298 enable_systrace_ = enable_systrace;
300 bool enable_argument_filter;
301 if (!dict->GetBoolean(kEnableArgumentFilterParam, &enable_argument_filter))
302 enable_argument_filter_ = false;
303 else
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);
320 else
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())
337 continue;
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);
357 } else {
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))
401 continue;
402 if (category.compare(0, strlen(TRACE_DISABLED_BY_DEFAULT("")),
403 TRACE_DISABLED_BY_DEFAULT("")) == 0) {
404 disabled_categories_.push_back(category);
405 } else {
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) {
424 std::string delay;
425 if (!list.GetString(i, &delay))
426 continue;
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,
437 const char* param,
438 const StringList& categories) const {
439 if (categories.empty())
440 return;
442 scoped_ptr<base::ListValue> list(new base::ListValue());
443 for (StringList::const_iterator ci = categories.begin();
444 ci != categories.end();
445 ++ci) {
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) {
459 return;
462 for (size_t i = 0; i < trigger_list->GetSize(); ++i) {
463 const base::DictionaryValue* trigger = nullptr;
464 if (!trigger_list->GetDictionary(i, &trigger))
465 continue;
467 MemoryDumpTriggerConfig dump_config;
468 std::string dump_type;
469 int interval = 0;
471 if (!trigger->GetInteger(kPeriodicIntervalParam, &interval)) {
472 continue;
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);
498 break;
499 case RECORD_CONTINUOUSLY:
500 dict.SetString(kRecordModeParam, kRecordContinuously);
501 break;
502 case RECORD_AS_MUCH_AS_POSSIBLE:
503 dict.SetString(kRecordModeParam, kRecordAsMuchAsPossible);
504 break;
505 case ECHO_TO_CONSOLE:
506 dict.SetString(kRecordModeParam, kTraceToConsole);
507 break;
508 default:
509 NOTREACHED();
512 if (enable_sampling_)
513 dict.SetBoolean(kEnableSamplingParam, true);
514 else
515 dict.SetBoolean(kEnableSamplingParam, false);
517 if (enable_systrace_)
518 dict.SetBoolean(kEnableSystraceParam, true);
519 else
520 dict.SetBoolean(kEnableSystraceParam, false);
522 if (enable_argument_filter_)
523 dict.SetBoolean(kEnableArgumentFilterParam, true);
524 else
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);
548 break;
549 case MemoryDumpArgs::LevelOfDetail::HIGH:
550 trigger_dict->SetString(kModeParam, kDetailedParam);
551 break;
552 default:
553 NOTREACHED();
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 {
566 std::string ret;
567 switch (record_mode_) {
568 case RECORD_UNTIL_FULL:
569 ret = kRecordUntilFull;
570 break;
571 case RECORD_CONTINUOUSLY:
572 ret = kRecordContinuously;
573 break;
574 case RECORD_AS_MUCH_AS_POSSIBLE:
575 ret = kRecordAsMuchAsPossible;
576 break;
577 case ECHO_TO_CONSOLE:
578 ret = kTraceToConsole;
579 break;
580 default:
581 NOTREACHED();
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;
589 return ret;
592 void TraceConfig::WriteCategoryFilterString(const StringList& values,
593 std::string* out,
594 bool included) const {
595 bool prepend_comma = !out->empty();
596 int token_cnt = 0;
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());
602 ++token_cnt;
606 void TraceConfig::WriteCategoryFilterString(const StringList& delays,
607 std::string* out) const {
608 bool prepend_comma = !out->empty();
609 int token_cnt = 0;
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,
615 ci->c_str());
616 ++token_cnt;
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();
627 ++ci) {
628 if (base::MatchPattern(category_name, ci->c_str()))
629 return true;
632 if (base::MatchPattern(category_name, TRACE_DISABLED_BY_DEFAULT("*")))
633 return false;
635 for (ci = included_categories_.begin();
636 ci != included_categories_.end();
637 ++ci) {
638 if (base::MatchPattern(category_name, ci->c_str()))
639 return true;
642 return false;
645 bool TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
646 const std::string& str) {
647 return str.empty() ||
648 str.at(0) == ' ' ||
649 str.at(str.length() - 1) == ' ';
652 bool TraceConfig::HasIncludedPatterns() const {
653 return !included_categories_.empty();
656 } // namespace trace_event
657 } // namespace base