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.
4 
5 #include "base/trace_event/trace_config.h"
6 
7 #include <stddef.h>
8 
9 #include <utility>
10 
11 #include "base/json/json_reader.h"
12 #include "base/json/json_writer.h"
13 #include "base/strings/pattern.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_tokenizer.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/trace_event/memory_dump_manager.h"
18 #include "base/trace_event/memory_dump_request_args.h"
19 #include "base/trace_event/trace_event.h"
20 
21 namespace base {
22 namespace trace_event {
23 
24 namespace {
25 
26 // String options that can be used to initialize TraceOptions.
27 const char kRecordUntilFull[] = "record-until-full";
28 const char kRecordContinuously[] = "record-continuously";
29 const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible";
30 const char kTraceToConsole[] = "trace-to-console";
31 const char kEnableSampling[] = "enable-sampling";
32 const char kEnableSystrace[] = "enable-systrace";
33 const char kEnableArgumentFilter[] = "enable-argument-filter";
34 
35 // String parameters that can be used to parse the trace config string.
36 const char kRecordModeParam[] = "record_mode";
37 const char kEnableSamplingParam[] = "enable_sampling";
38 const char kEnableSystraceParam[] = "enable_systrace";
39 const char kEnableArgumentFilterParam[] = "enable_argument_filter";
40 const char kIncludedCategoriesParam[] = "included_categories";
41 const char kExcludedCategoriesParam[] = "excluded_categories";
42 const char kSyntheticDelaysParam[] = "synthetic_delays";
43 
44 const char kSyntheticDelayCategoryFilterPrefix[] = "DELAY(";
45 
46 // String parameters that is used to parse memory dump config in trace config
47 // string.
48 const char kMemoryDumpConfigParam[] = "memory_dump_config";
49 const char kTriggersParam[] = "triggers";
50 const char kPeriodicIntervalParam[] = "periodic_interval_ms";
51 const char kModeParam[] = "mode";
52 
53 // Default configuration of memory dumps.
54 const TraceConfig::MemoryDumpTriggerConfig kDefaultHeavyMemoryDumpTrigger = {
55     2000,  // periodic_interval_ms
56     MemoryDumpLevelOfDetail::DETAILED};
57 const TraceConfig::MemoryDumpTriggerConfig kDefaultLightMemoryDumpTrigger = {
58     250,  // periodic_interval_ms
59     MemoryDumpLevelOfDetail::LIGHT};
60 
61 class ConvertableTraceConfigToTraceFormat
62     : public base::trace_event::ConvertableToTraceFormat {
63  public:
ConvertableTraceConfigToTraceFormat(const TraceConfig & trace_config)64   explicit ConvertableTraceConfigToTraceFormat(const TraceConfig& trace_config)
65       : trace_config_(trace_config) {}
AppendAsTraceFormat(std::string * out) const66   void AppendAsTraceFormat(std::string* out) const override {
67     out->append(trace_config_.ToString());
68   }
69 
70  protected:
~ConvertableTraceConfigToTraceFormat()71   ~ConvertableTraceConfigToTraceFormat() override {}
72 
73  private:
74   const TraceConfig trace_config_;
75 };
76 
77 }  // namespace
78 
TraceConfig()79 TraceConfig::TraceConfig() {
80   InitializeDefault();
81 }
82 
TraceConfig(const std::string & category_filter_string,const std::string & trace_options_string)83 TraceConfig::TraceConfig(const std::string& category_filter_string,
84                          const std::string& trace_options_string) {
85   InitializeFromStrings(category_filter_string, trace_options_string);
86 }
87 
TraceConfig(const std::string & category_filter_string,TraceRecordMode record_mode)88 TraceConfig::TraceConfig(const std::string& category_filter_string,
89                          TraceRecordMode record_mode) {
90   std::string trace_options_string;
91   switch (record_mode) {
92     case RECORD_UNTIL_FULL:
93       trace_options_string = kRecordUntilFull;
94       break;
95     case RECORD_CONTINUOUSLY:
96       trace_options_string = kRecordContinuously;
97       break;
98     case RECORD_AS_MUCH_AS_POSSIBLE:
99       trace_options_string = kRecordAsMuchAsPossible;
100       break;
101     case ECHO_TO_CONSOLE:
102       trace_options_string = kTraceToConsole;
103       break;
104     default:
105       NOTREACHED();
106   }
107   InitializeFromStrings(category_filter_string, trace_options_string);
108 }
109 
TraceConfig(const std::string & config_string)110 TraceConfig::TraceConfig(const std::string& config_string) {
111   if (!config_string.empty())
112     InitializeFromConfigString(config_string);
113   else
114     InitializeDefault();
115 }
116 
TraceConfig(const TraceConfig & tc)117 TraceConfig::TraceConfig(const TraceConfig& tc)
118     : record_mode_(tc.record_mode_),
119       enable_sampling_(tc.enable_sampling_),
120       enable_systrace_(tc.enable_systrace_),
121       enable_argument_filter_(tc.enable_argument_filter_),
122       memory_dump_config_(tc.memory_dump_config_),
123       included_categories_(tc.included_categories_),
124       disabled_categories_(tc.disabled_categories_),
125       excluded_categories_(tc.excluded_categories_),
126       synthetic_delays_(tc.synthetic_delays_) {}
127 
~TraceConfig()128 TraceConfig::~TraceConfig() {
129 }
130 
operator =(const TraceConfig & rhs)131 TraceConfig& TraceConfig::operator=(const TraceConfig& rhs) {
132   if (this == &rhs)
133     return *this;
134 
135   record_mode_ = rhs.record_mode_;
136   enable_sampling_ = rhs.enable_sampling_;
137   enable_systrace_ = rhs.enable_systrace_;
138   enable_argument_filter_ = rhs.enable_argument_filter_;
139   memory_dump_config_ = rhs.memory_dump_config_;
140   included_categories_ = rhs.included_categories_;
141   disabled_categories_ = rhs.disabled_categories_;
142   excluded_categories_ = rhs.excluded_categories_;
143   synthetic_delays_ = rhs.synthetic_delays_;
144   return *this;
145 }
146 
GetSyntheticDelayValues() const147 const TraceConfig::StringList& TraceConfig::GetSyntheticDelayValues() const {
148   return synthetic_delays_;
149 }
150 
ToString() const151 std::string TraceConfig::ToString() const {
152   base::DictionaryValue dict;
153   ToDict(dict);
154 
155   std::string json;
156   base::JSONWriter::Write(dict, &json);
157 
158   return json;
159 }
160 
161 scoped_refptr<ConvertableToTraceFormat>
AsConvertableToTraceFormat() const162 TraceConfig::AsConvertableToTraceFormat() const {
163   return new ConvertableTraceConfigToTraceFormat(*this);
164 }
165 
ToCategoryFilterString() const166 std::string TraceConfig::ToCategoryFilterString() const {
167   std::string filter_string;
168   WriteCategoryFilterString(included_categories_, &filter_string, true);
169   WriteCategoryFilterString(disabled_categories_, &filter_string, true);
170   WriteCategoryFilterString(excluded_categories_, &filter_string, false);
171   WriteCategoryFilterString(synthetic_delays_, &filter_string);
172   return filter_string;
173 }
174 
IsCategoryGroupEnabled(const char * category_group_name) const175 bool TraceConfig::IsCategoryGroupEnabled(
176     const char* category_group_name) const {
177   // TraceLog should call this method only as part of enabling/disabling
178   // categories.
179 
180   bool had_enabled_by_default = false;
181   DCHECK(category_group_name);
182   CStringTokenizer category_group_tokens(
183       category_group_name, category_group_name + strlen(category_group_name),
184       ",");
185   while (category_group_tokens.GetNext()) {
186     std::string category_group_token = category_group_tokens.token();
187     // Don't allow empty tokens, nor tokens with leading or trailing space.
188     DCHECK(!TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
189                category_group_token))
190         << "Disallowed category string";
191     if (IsCategoryEnabled(category_group_token.c_str())) {
192       return true;
193     }
194     if (!base::MatchPattern(category_group_token.c_str(),
195                             TRACE_DISABLED_BY_DEFAULT("*")))
196       had_enabled_by_default = true;
197   }
198   // Do a second pass to check for explicitly disabled categories
199   // (those explicitly enabled have priority due to first pass).
200   category_group_tokens.Reset();
201   bool category_group_disabled = false;
202   while (category_group_tokens.GetNext()) {
203     std::string category_group_token = category_group_tokens.token();
204     for (StringList::const_iterator ci = excluded_categories_.begin();
205          ci != excluded_categories_.end();
206          ++ci) {
207       if (base::MatchPattern(category_group_token.c_str(), ci->c_str())) {
208         // Current token of category_group_name is present in excluded_list.
209         // Flag the exclusion and proceed further to check if any of the
210         // remaining categories of category_group_name is not present in the
211         // excluded_ list.
212         category_group_disabled = true;
213         break;
214       }
215       // One of the category of category_group_name is not present in
216       // excluded_ list. So, if it's not a disabled-by-default category,
217       // it has to be included_ list. Enable the category_group_name
218       // for recording.
219       if (!base::MatchPattern(category_group_token.c_str(),
220                               TRACE_DISABLED_BY_DEFAULT("*"))) {
221         category_group_disabled = false;
222       }
223     }
224     // One of the categories present in category_group_name is not present in
225     // excluded_ list. Implies this category_group_name group can be enabled
226     // for recording, since one of its groups is enabled for recording.
227     if (!category_group_disabled)
228       break;
229   }
230   // If the category group is not excluded, and there are no included patterns
231   // we consider this category group enabled, as long as it had categories
232   // other than disabled-by-default.
233   return !category_group_disabled &&
234          included_categories_.empty() && had_enabled_by_default;
235 }
236 
Merge(const TraceConfig & config)237 void TraceConfig::Merge(const TraceConfig& config) {
238   if (record_mode_ != config.record_mode_
239       || enable_sampling_ != config.enable_sampling_
240       || enable_systrace_ != config.enable_systrace_
241       || enable_argument_filter_ != config.enable_argument_filter_) {
242     DLOG(ERROR) << "Attempting to merge trace config with a different "
243                 << "set of options.";
244   }
245 
246   // Keep included patterns only if both filters have an included entry.
247   // Otherwise, one of the filter was specifying "*" and we want to honor the
248   // broadest filter.
249   if (HasIncludedPatterns() && config.HasIncludedPatterns()) {
250     included_categories_.insert(included_categories_.end(),
251                                 config.included_categories_.begin(),
252                                 config.included_categories_.end());
253   } else {
254     included_categories_.clear();
255   }
256 
257   memory_dump_config_.insert(memory_dump_config_.end(),
258                              config.memory_dump_config_.begin(),
259                              config.memory_dump_config_.end());
260 
261   disabled_categories_.insert(disabled_categories_.end(),
262                               config.disabled_categories_.begin(),
263                               config.disabled_categories_.end());
264   excluded_categories_.insert(excluded_categories_.end(),
265                               config.excluded_categories_.begin(),
266                               config.excluded_categories_.end());
267   synthetic_delays_.insert(synthetic_delays_.end(),
268                            config.synthetic_delays_.begin(),
269                            config.synthetic_delays_.end());
270 }
271 
Clear()272 void TraceConfig::Clear() {
273   record_mode_ = RECORD_UNTIL_FULL;
274   enable_sampling_ = false;
275   enable_systrace_ = false;
276   enable_argument_filter_ = false;
277   included_categories_.clear();
278   disabled_categories_.clear();
279   excluded_categories_.clear();
280   synthetic_delays_.clear();
281   memory_dump_config_.clear();
282 }
283 
InitializeDefault()284 void TraceConfig::InitializeDefault() {
285   record_mode_ = RECORD_UNTIL_FULL;
286   enable_sampling_ = false;
287   enable_systrace_ = false;
288   enable_argument_filter_ = false;
289   excluded_categories_.push_back("*Debug");
290   excluded_categories_.push_back("*Test");
291 }
292 
InitializeFromConfigString(const std::string & config_string)293 void TraceConfig::InitializeFromConfigString(const std::string& config_string) {
294   scoped_ptr<base::Value> value(base::JSONReader::Read(config_string));
295   if (!value || !value->IsType(base::Value::TYPE_DICTIONARY)) {
296     InitializeDefault();
297     return;
298   }
299   scoped_ptr<base::DictionaryValue> dict(
300         static_cast<base::DictionaryValue*>(value.release()));
301 
302   record_mode_ = RECORD_UNTIL_FULL;
303   std::string record_mode;
304   if (dict->GetString(kRecordModeParam, &record_mode)) {
305     if (record_mode == kRecordUntilFull) {
306       record_mode_ = RECORD_UNTIL_FULL;
307     } else if (record_mode == kRecordContinuously) {
308       record_mode_ = RECORD_CONTINUOUSLY;
309     } else if (record_mode == kTraceToConsole) {
310       record_mode_ = ECHO_TO_CONSOLE;
311     } else if (record_mode == kRecordAsMuchAsPossible) {
312       record_mode_ = RECORD_AS_MUCH_AS_POSSIBLE;
313     }
314   }
315 
316   bool enable_sampling;
317   if (!dict->GetBoolean(kEnableSamplingParam, &enable_sampling))
318     enable_sampling_ = false;
319   else
320     enable_sampling_ = enable_sampling;
321 
322   bool enable_systrace;
323   if (!dict->GetBoolean(kEnableSystraceParam, &enable_systrace))
324     enable_systrace_ = false;
325   else
326     enable_systrace_ = enable_systrace;
327 
328   bool enable_argument_filter;
329   if (!dict->GetBoolean(kEnableArgumentFilterParam, &enable_argument_filter))
330     enable_argument_filter_ = false;
331   else
332     enable_argument_filter_ = enable_argument_filter;
333 
334   base::ListValue* category_list = nullptr;
335   if (dict->GetList(kIncludedCategoriesParam, &category_list))
336     SetCategoriesFromIncludedList(*category_list);
337   if (dict->GetList(kExcludedCategoriesParam, &category_list))
338     SetCategoriesFromExcludedList(*category_list);
339   if (dict->GetList(kSyntheticDelaysParam, &category_list))
340     SetSyntheticDelaysFromList(*category_list);
341 
342   if (IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
343     // If dump triggers not set, the client is using the legacy with just
344     // category enabled. So, use the default periodic dump config.
345     base::DictionaryValue* memory_dump_config = nullptr;
346     if (dict->GetDictionary(kMemoryDumpConfigParam, &memory_dump_config))
347       SetMemoryDumpConfig(*memory_dump_config);
348     else
349       SetDefaultMemoryDumpConfig();
350   }
351 }
352 
InitializeFromStrings(const std::string & category_filter_string,const std::string & trace_options_string)353 void TraceConfig::InitializeFromStrings(
354     const std::string& category_filter_string,
355     const std::string& trace_options_string) {
356   if (!category_filter_string.empty()) {
357     std::vector<std::string> split = base::SplitString(
358         category_filter_string, ",", base::TRIM_WHITESPACE,
359         base::SPLIT_WANT_ALL);
360     std::vector<std::string>::iterator iter;
361     for (iter = split.begin(); iter != split.end(); ++iter) {
362       std::string category = *iter;
363       // Ignore empty categories.
364       if (category.empty())
365         continue;
366       // Synthetic delays are of the form 'DELAY(delay;option;option;...)'.
367       if (category.find(kSyntheticDelayCategoryFilterPrefix) == 0 &&
368           category.at(category.size() - 1) == ')') {
369         category = category.substr(
370             strlen(kSyntheticDelayCategoryFilterPrefix),
371             category.size() - strlen(kSyntheticDelayCategoryFilterPrefix) - 1);
372         size_t name_length = category.find(';');
373         if (name_length != std::string::npos && name_length > 0 &&
374             name_length != category.size() - 1) {
375           synthetic_delays_.push_back(category);
376         }
377       } else if (category.at(0) == '-') {
378         // Excluded categories start with '-'.
379         // Remove '-' from category string.
380         category = category.substr(1);
381         excluded_categories_.push_back(category);
382       } else if (category.compare(0, strlen(TRACE_DISABLED_BY_DEFAULT("")),
383                                   TRACE_DISABLED_BY_DEFAULT("")) == 0) {
384         disabled_categories_.push_back(category);
385       } else {
386         included_categories_.push_back(category);
387       }
388     }
389   }
390 
391   record_mode_ = RECORD_UNTIL_FULL;
392   enable_sampling_ = false;
393   enable_systrace_ = false;
394   enable_argument_filter_ = false;
395   if(!trace_options_string.empty()) {
396     std::vector<std::string> split = base::SplitString(
397         trace_options_string, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
398     std::vector<std::string>::iterator iter;
399     for (iter = split.begin(); iter != split.end(); ++iter) {
400       if (*iter == kRecordUntilFull) {
401         record_mode_ = RECORD_UNTIL_FULL;
402       } else if (*iter == kRecordContinuously) {
403         record_mode_ = RECORD_CONTINUOUSLY;
404       } else if (*iter == kTraceToConsole) {
405         record_mode_ = ECHO_TO_CONSOLE;
406       } else if (*iter == kRecordAsMuchAsPossible) {
407         record_mode_ = RECORD_AS_MUCH_AS_POSSIBLE;
408       } else if (*iter == kEnableSampling) {
409         enable_sampling_ = true;
410       } else if (*iter == kEnableSystrace) {
411         enable_systrace_ = true;
412       } else if (*iter == kEnableArgumentFilter) {
413         enable_argument_filter_ = true;
414       }
415     }
416   }
417 
418   if (IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
419     SetDefaultMemoryDumpConfig();
420   }
421 }
422 
SetCategoriesFromIncludedList(const base::ListValue & included_list)423 void TraceConfig::SetCategoriesFromIncludedList(
424     const base::ListValue& included_list) {
425   included_categories_.clear();
426   for (size_t i = 0; i < included_list.GetSize(); ++i) {
427     std::string category;
428     if (!included_list.GetString(i, &category))
429       continue;
430     if (category.compare(0, strlen(TRACE_DISABLED_BY_DEFAULT("")),
431                          TRACE_DISABLED_BY_DEFAULT("")) == 0) {
432       disabled_categories_.push_back(category);
433     } else {
434       included_categories_.push_back(category);
435     }
436   }
437 }
438 
SetCategoriesFromExcludedList(const base::ListValue & excluded_list)439 void TraceConfig::SetCategoriesFromExcludedList(
440     const base::ListValue& excluded_list) {
441   excluded_categories_.clear();
442   for (size_t i = 0; i < excluded_list.GetSize(); ++i) {
443     std::string category;
444     if (excluded_list.GetString(i, &category))
445       excluded_categories_.push_back(category);
446   }
447 }
448 
SetSyntheticDelaysFromList(const base::ListValue & list)449 void TraceConfig::SetSyntheticDelaysFromList(const base::ListValue& list) {
450   synthetic_delays_.clear();
451   for (size_t i = 0; i < list.GetSize(); ++i) {
452     std::string delay;
453     if (!list.GetString(i, &delay))
454       continue;
455     // Synthetic delays are of the form "delay;option;option;...".
456     size_t name_length = delay.find(';');
457     if (name_length != std::string::npos && name_length > 0 &&
458         name_length != delay.size() - 1) {
459       synthetic_delays_.push_back(delay);
460     }
461   }
462 }
463 
AddCategoryToDict(base::DictionaryValue & dict,const char * param,const StringList & categories) const464 void TraceConfig::AddCategoryToDict(base::DictionaryValue& dict,
465                                     const char* param,
466                                     const StringList& categories) const {
467   if (categories.empty())
468     return;
469 
470   scoped_ptr<base::ListValue> list(new base::ListValue());
471   for (StringList::const_iterator ci = categories.begin();
472        ci != categories.end();
473        ++ci) {
474     list->AppendString(*ci);
475   }
476 
477   dict.Set(param, std::move(list));
478 }
479 
SetMemoryDumpConfig(const base::DictionaryValue & memory_dump_config)480 void TraceConfig::SetMemoryDumpConfig(
481     const base::DictionaryValue& memory_dump_config) {
482   memory_dump_config_.clear();
483 
484   const base::ListValue* trigger_list = nullptr;
485   if (!memory_dump_config.GetList(kTriggersParam, &trigger_list) ||
486       trigger_list->GetSize() == 0) {
487     return;
488   }
489 
490   for (size_t i = 0; i < trigger_list->GetSize(); ++i) {
491     const base::DictionaryValue* trigger = nullptr;
492     if (!trigger_list->GetDictionary(i, &trigger))
493       continue;
494 
495     MemoryDumpTriggerConfig dump_config;
496     int interval = 0;
497 
498     if (!trigger->GetInteger(kPeriodicIntervalParam, &interval)) {
499       continue;
500     }
501     DCHECK_GT(interval, 0);
502     dump_config.periodic_interval_ms = static_cast<uint32_t>(interval);
503     std::string level_of_detail_str;
504     trigger->GetString(kModeParam, &level_of_detail_str);
505     dump_config.level_of_detail =
506         StringToMemoryDumpLevelOfDetail(level_of_detail_str);
507     memory_dump_config_.push_back(dump_config);
508   }
509 }
510 
SetDefaultMemoryDumpConfig()511 void TraceConfig::SetDefaultMemoryDumpConfig() {
512   memory_dump_config_.clear();
513   memory_dump_config_.push_back(kDefaultHeavyMemoryDumpTrigger);
514   memory_dump_config_.push_back(kDefaultLightMemoryDumpTrigger);
515 }
516 
ToDict(base::DictionaryValue & dict) const517 void TraceConfig::ToDict(base::DictionaryValue& dict) const {
518   switch (record_mode_) {
519     case RECORD_UNTIL_FULL:
520       dict.SetString(kRecordModeParam, kRecordUntilFull);
521       break;
522     case RECORD_CONTINUOUSLY:
523       dict.SetString(kRecordModeParam, kRecordContinuously);
524       break;
525     case RECORD_AS_MUCH_AS_POSSIBLE:
526       dict.SetString(kRecordModeParam, kRecordAsMuchAsPossible);
527       break;
528     case ECHO_TO_CONSOLE:
529       dict.SetString(kRecordModeParam, kTraceToConsole);
530       break;
531     default:
532       NOTREACHED();
533   }
534 
535   if (enable_sampling_)
536     dict.SetBoolean(kEnableSamplingParam, true);
537   else
538     dict.SetBoolean(kEnableSamplingParam, false);
539 
540   if (enable_systrace_)
541     dict.SetBoolean(kEnableSystraceParam, true);
542   else
543     dict.SetBoolean(kEnableSystraceParam, false);
544 
545   if (enable_argument_filter_)
546     dict.SetBoolean(kEnableArgumentFilterParam, true);
547   else
548     dict.SetBoolean(kEnableArgumentFilterParam, false);
549 
550   StringList categories(included_categories_);
551   categories.insert(categories.end(),
552                     disabled_categories_.begin(),
553                     disabled_categories_.end());
554   AddCategoryToDict(dict, kIncludedCategoriesParam, categories);
555   AddCategoryToDict(dict, kExcludedCategoriesParam, excluded_categories_);
556   AddCategoryToDict(dict, kSyntheticDelaysParam, synthetic_delays_);
557 
558   if (IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
559     scoped_ptr<base::DictionaryValue> memory_dump_config(
560         new base::DictionaryValue());
561     scoped_ptr<base::ListValue> triggers_list(new base::ListValue());
562     for (const MemoryDumpTriggerConfig& config : memory_dump_config_) {
563       scoped_ptr<base::DictionaryValue> trigger_dict(
564           new base::DictionaryValue());
565       trigger_dict->SetInteger(kPeriodicIntervalParam,
566                                static_cast<int>(config.periodic_interval_ms));
567       trigger_dict->SetString(
568           kModeParam, MemoryDumpLevelOfDetailToString(config.level_of_detail));
569       triggers_list->Append(std::move(trigger_dict));
570     }
571 
572     // Empty triggers will still be specified explicitly since it means that
573     // the periodic dumps are not enabled.
574     memory_dump_config->Set(kTriggersParam, std::move(triggers_list));
575     dict.Set(kMemoryDumpConfigParam, std::move(memory_dump_config));
576   }
577 }
578 
ToTraceOptionsString() const579 std::string TraceConfig::ToTraceOptionsString() const {
580   std::string ret;
581   switch (record_mode_) {
582     case RECORD_UNTIL_FULL:
583       ret = kRecordUntilFull;
584       break;
585     case RECORD_CONTINUOUSLY:
586       ret = kRecordContinuously;
587       break;
588     case RECORD_AS_MUCH_AS_POSSIBLE:
589       ret = kRecordAsMuchAsPossible;
590       break;
591     case ECHO_TO_CONSOLE:
592       ret = kTraceToConsole;
593       break;
594     default:
595       NOTREACHED();
596   }
597   if (enable_sampling_)
598     ret = ret + "," + kEnableSampling;
599   if (enable_systrace_)
600     ret = ret + "," + kEnableSystrace;
601   if (enable_argument_filter_)
602     ret = ret + "," + kEnableArgumentFilter;
603   return ret;
604 }
605 
WriteCategoryFilterString(const StringList & values,std::string * out,bool included) const606 void TraceConfig::WriteCategoryFilterString(const StringList& values,
607                                             std::string* out,
608                                             bool included) const {
609   bool prepend_comma = !out->empty();
610   int token_cnt = 0;
611   for (StringList::const_iterator ci = values.begin();
612        ci != values.end(); ++ci) {
613     if (token_cnt > 0 || prepend_comma)
614       StringAppendF(out, ",");
615     StringAppendF(out, "%s%s", (included ? "" : "-"), ci->c_str());
616     ++token_cnt;
617   }
618 }
619 
WriteCategoryFilterString(const StringList & delays,std::string * out) const620 void TraceConfig::WriteCategoryFilterString(const StringList& delays,
621                                             std::string* out) const {
622   bool prepend_comma = !out->empty();
623   int token_cnt = 0;
624   for (StringList::const_iterator ci = delays.begin();
625        ci != delays.end(); ++ci) {
626     if (token_cnt > 0 || prepend_comma)
627       StringAppendF(out, ",");
628     StringAppendF(out, "%s%s)", kSyntheticDelayCategoryFilterPrefix,
629                   ci->c_str());
630     ++token_cnt;
631   }
632 }
633 
IsCategoryEnabled(const char * category_name) const634 bool TraceConfig::IsCategoryEnabled(const char* category_name) const {
635   StringList::const_iterator ci;
636 
637   // Check the disabled- filters and the disabled-* wildcard first so that a
638   // "*" filter does not include the disabled.
639   for (ci = disabled_categories_.begin();
640        ci != disabled_categories_.end();
641        ++ci) {
642     if (base::MatchPattern(category_name, ci->c_str()))
643       return true;
644   }
645 
646   if (base::MatchPattern(category_name, TRACE_DISABLED_BY_DEFAULT("*")))
647     return false;
648 
649   for (ci = included_categories_.begin();
650        ci != included_categories_.end();
651        ++ci) {
652     if (base::MatchPattern(category_name, ci->c_str()))
653       return true;
654   }
655 
656   return false;
657 }
658 
IsEmptyOrContainsLeadingOrTrailingWhitespace(const std::string & str)659 bool TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
660     const std::string& str) {
661   return  str.empty() ||
662           str.at(0) == ' ' ||
663           str.at(str.length() - 1) == ' ';
664 }
665 
HasIncludedPatterns() const666 bool TraceConfig::HasIncludedPatterns() const {
667   return !included_categories_.empty();
668 }
669 
670 }  // namespace trace_event
671 }  // namespace base
672