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