Home | History | Annotate | Download | only in trace_event
      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/string_split.h"
     15 #include "base/trace_event/memory_dump_manager.h"
     16 #include "base/trace_event/memory_dump_request_args.h"
     17 #include "base/trace_event/trace_event.h"
     18 
     19 namespace base {
     20 namespace trace_event {
     21 
     22 namespace {
     23 
     24 // String options that can be used to initialize TraceOptions.
     25 const char kRecordUntilFull[] = "record-until-full";
     26 const char kRecordContinuously[] = "record-continuously";
     27 const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible";
     28 const char kTraceToConsole[] = "trace-to-console";
     29 const char kEnableSystrace[] = "enable-systrace";
     30 const char kEnableArgumentFilter[] = "enable-argument-filter";
     31 
     32 // String parameters that can be used to parse the trace config string.
     33 const char kRecordModeParam[] = "record_mode";
     34 const char kEnableSystraceParam[] = "enable_systrace";
     35 const char kEnableArgumentFilterParam[] = "enable_argument_filter";
     36 
     37 // String parameters that is used to parse memory dump config in trace config
     38 // string.
     39 const char kMemoryDumpConfigParam[] = "memory_dump_config";
     40 const char kAllowedDumpModesParam[] = "allowed_dump_modes";
     41 const char kTriggersParam[] = "triggers";
     42 const char kTriggerModeParam[] = "mode";
     43 const char kMinTimeBetweenDumps[] = "min_time_between_dumps_ms";
     44 const char kTriggerTypeParam[] = "type";
     45 const char kPeriodicIntervalLegacyParam[] = "periodic_interval_ms";
     46 const char kHeapProfilerOptions[] = "heap_profiler_options";
     47 const char kBreakdownThresholdBytes[] = "breakdown_threshold_bytes";
     48 
     49 // String parameters used to parse category event filters.
     50 const char kEventFiltersParam[] = "event_filters";
     51 const char kFilterPredicateParam[] = "filter_predicate";
     52 const char kFilterArgsParam[] = "filter_args";
     53 
     54 // Default configuration of memory dumps.
     55 const TraceConfig::MemoryDumpConfig::Trigger kDefaultHeavyMemoryDumpTrigger = {
     56     2000,  // min_time_between_dumps_ms
     57     MemoryDumpLevelOfDetail::DETAILED, MemoryDumpType::PERIODIC_INTERVAL};
     58 const TraceConfig::MemoryDumpConfig::Trigger kDefaultLightMemoryDumpTrigger = {
     59     250,  // min_time_between_dumps_ms
     60     MemoryDumpLevelOfDetail::LIGHT, MemoryDumpType::PERIODIC_INTERVAL};
     61 
     62 class ConvertableTraceConfigToTraceFormat
     63     : public base::trace_event::ConvertableToTraceFormat {
     64  public:
     65   explicit ConvertableTraceConfigToTraceFormat(const TraceConfig& trace_config)
     66       : trace_config_(trace_config) {}
     67 
     68   ~ConvertableTraceConfigToTraceFormat() override {}
     69 
     70   void AppendAsTraceFormat(std::string* out) const override {
     71     out->append(trace_config_.ToString());
     72   }
     73 
     74  private:
     75   const TraceConfig trace_config_;
     76 };
     77 
     78 std::set<MemoryDumpLevelOfDetail> GetDefaultAllowedMemoryDumpModes() {
     79   std::set<MemoryDumpLevelOfDetail> all_modes;
     80   for (uint32_t mode = static_cast<uint32_t>(MemoryDumpLevelOfDetail::FIRST);
     81        mode <= static_cast<uint32_t>(MemoryDumpLevelOfDetail::LAST); mode++) {
     82     all_modes.insert(static_cast<MemoryDumpLevelOfDetail>(mode));
     83   }
     84   return all_modes;
     85 }
     86 
     87 }  // namespace
     88 
     89 TraceConfig::MemoryDumpConfig::HeapProfiler::HeapProfiler()
     90     : breakdown_threshold_bytes(kDefaultBreakdownThresholdBytes) {}
     91 
     92 void TraceConfig::MemoryDumpConfig::HeapProfiler::Clear() {
     93   breakdown_threshold_bytes = kDefaultBreakdownThresholdBytes;
     94 }
     95 
     96 void TraceConfig::ResetMemoryDumpConfig(
     97     const TraceConfig::MemoryDumpConfig& memory_dump_config) {
     98   memory_dump_config_.Clear();
     99   memory_dump_config_ = memory_dump_config;
    100 }
    101 
    102 TraceConfig::MemoryDumpConfig::MemoryDumpConfig() {}
    103 
    104 TraceConfig::MemoryDumpConfig::MemoryDumpConfig(
    105     const MemoryDumpConfig& other) = default;
    106 
    107 TraceConfig::MemoryDumpConfig::~MemoryDumpConfig() {}
    108 
    109 void TraceConfig::MemoryDumpConfig::Clear() {
    110   allowed_dump_modes.clear();
    111   triggers.clear();
    112   heap_profiler_options.Clear();
    113 }
    114 
    115 void TraceConfig::MemoryDumpConfig::Merge(
    116     const TraceConfig::MemoryDumpConfig& config) {
    117   triggers.insert(triggers.end(), config.triggers.begin(),
    118                   config.triggers.end());
    119   allowed_dump_modes.insert(config.allowed_dump_modes.begin(),
    120                             config.allowed_dump_modes.end());
    121   heap_profiler_options.breakdown_threshold_bytes =
    122       std::min(heap_profiler_options.breakdown_threshold_bytes,
    123                config.heap_profiler_options.breakdown_threshold_bytes);
    124 }
    125 
    126 TraceConfig::EventFilterConfig::EventFilterConfig(
    127     const std::string& predicate_name)
    128     : predicate_name_(predicate_name) {}
    129 
    130 TraceConfig::EventFilterConfig::~EventFilterConfig() {}
    131 
    132 TraceConfig::EventFilterConfig::EventFilterConfig(const EventFilterConfig& tc) {
    133   *this = tc;
    134 }
    135 
    136 TraceConfig::EventFilterConfig& TraceConfig::EventFilterConfig::operator=(
    137     const TraceConfig::EventFilterConfig& rhs) {
    138   if (this == &rhs)
    139     return *this;
    140 
    141   predicate_name_ = rhs.predicate_name_;
    142   category_filter_ = rhs.category_filter_;
    143 
    144   if (rhs.args_)
    145     args_ = rhs.args_->CreateDeepCopy();
    146 
    147   return *this;
    148 }
    149 
    150 void TraceConfig::EventFilterConfig::InitializeFromConfigDict(
    151     const base::DictionaryValue* event_filter) {
    152   category_filter_.InitializeFromConfigDict(*event_filter);
    153 
    154   const base::DictionaryValue* args_dict = nullptr;
    155   if (event_filter->GetDictionary(kFilterArgsParam, &args_dict))
    156     args_ = args_dict->CreateDeepCopy();
    157 }
    158 
    159 void TraceConfig::EventFilterConfig::SetCategoryFilter(
    160     const TraceConfigCategoryFilter& category_filter) {
    161   category_filter_ = category_filter;
    162 }
    163 
    164 void TraceConfig::EventFilterConfig::ToDict(
    165     DictionaryValue* filter_dict) const {
    166   filter_dict->SetString(kFilterPredicateParam, predicate_name());
    167 
    168   category_filter_.ToDict(filter_dict);
    169 
    170   if (args_)
    171     filter_dict->Set(kFilterArgsParam, args_->CreateDeepCopy());
    172 }
    173 
    174 bool TraceConfig::EventFilterConfig::GetArgAsSet(
    175     const char* key,
    176     std::unordered_set<std::string>* out_set) const {
    177   const ListValue* list = nullptr;
    178   if (!args_->GetList(key, &list))
    179     return false;
    180   for (size_t i = 0; i < list->GetSize(); ++i) {
    181     std::string value;
    182     if (list->GetString(i, &value))
    183       out_set->insert(value);
    184   }
    185   return true;
    186 }
    187 
    188 bool TraceConfig::EventFilterConfig::IsCategoryGroupEnabled(
    189     const StringPiece& category_group_name) const {
    190   return category_filter_.IsCategoryGroupEnabled(category_group_name);
    191 }
    192 
    193 TraceConfig::TraceConfig() {
    194   InitializeDefault();
    195 }
    196 
    197 TraceConfig::TraceConfig(StringPiece category_filter_string,
    198                          StringPiece trace_options_string) {
    199   InitializeFromStrings(category_filter_string, trace_options_string);
    200 }
    201 
    202 TraceConfig::TraceConfig(StringPiece category_filter_string,
    203                          TraceRecordMode record_mode) {
    204   std::string trace_options_string;
    205   switch (record_mode) {
    206     case RECORD_UNTIL_FULL:
    207       trace_options_string = kRecordUntilFull;
    208       break;
    209     case RECORD_CONTINUOUSLY:
    210       trace_options_string = kRecordContinuously;
    211       break;
    212     case RECORD_AS_MUCH_AS_POSSIBLE:
    213       trace_options_string = kRecordAsMuchAsPossible;
    214       break;
    215     case ECHO_TO_CONSOLE:
    216       trace_options_string = kTraceToConsole;
    217       break;
    218     default:
    219       NOTREACHED();
    220   }
    221   InitializeFromStrings(category_filter_string, trace_options_string);
    222 }
    223 
    224 TraceConfig::TraceConfig(const DictionaryValue& config) {
    225   InitializeFromConfigDict(config);
    226 }
    227 
    228 TraceConfig::TraceConfig(StringPiece config_string) {
    229   if (!config_string.empty())
    230     InitializeFromConfigString(config_string);
    231   else
    232     InitializeDefault();
    233 }
    234 
    235 TraceConfig::TraceConfig(const TraceConfig& tc)
    236     : record_mode_(tc.record_mode_),
    237       enable_systrace_(tc.enable_systrace_),
    238       enable_argument_filter_(tc.enable_argument_filter_),
    239       category_filter_(tc.category_filter_),
    240       memory_dump_config_(tc.memory_dump_config_),
    241       event_filters_(tc.event_filters_) {}
    242 
    243 TraceConfig::~TraceConfig() {
    244 }
    245 
    246 TraceConfig& TraceConfig::operator=(const TraceConfig& rhs) {
    247   if (this == &rhs)
    248     return *this;
    249 
    250   record_mode_ = rhs.record_mode_;
    251   enable_systrace_ = rhs.enable_systrace_;
    252   enable_argument_filter_ = rhs.enable_argument_filter_;
    253   category_filter_ = rhs.category_filter_;
    254   memory_dump_config_ = rhs.memory_dump_config_;
    255   event_filters_ = rhs.event_filters_;
    256   return *this;
    257 }
    258 
    259 const TraceConfig::StringList& TraceConfig::GetSyntheticDelayValues() const {
    260   return category_filter_.synthetic_delays();
    261 }
    262 
    263 std::string TraceConfig::ToString() const {
    264   std::unique_ptr<DictionaryValue> dict = ToDict();
    265   std::string json;
    266   JSONWriter::Write(*dict, &json);
    267   return json;
    268 }
    269 
    270 std::unique_ptr<ConvertableToTraceFormat>
    271 TraceConfig::AsConvertableToTraceFormat() const {
    272   return MakeUnique<ConvertableTraceConfigToTraceFormat>(*this);
    273 }
    274 
    275 std::string TraceConfig::ToCategoryFilterString() const {
    276   return category_filter_.ToFilterString();
    277 }
    278 
    279 bool TraceConfig::IsCategoryGroupEnabled(
    280     const StringPiece& category_group_name) const {
    281   // TraceLog should call this method only as part of enabling/disabling
    282   // categories.
    283   return category_filter_.IsCategoryGroupEnabled(category_group_name);
    284 }
    285 
    286 void TraceConfig::Merge(const TraceConfig& config) {
    287   if (record_mode_ != config.record_mode_
    288       || enable_systrace_ != config.enable_systrace_
    289       || enable_argument_filter_ != config.enable_argument_filter_) {
    290     DLOG(ERROR) << "Attempting to merge trace config with a different "
    291                 << "set of options.";
    292   }
    293 
    294   category_filter_.Merge(config.category_filter_);
    295 
    296   memory_dump_config_.Merge(config.memory_dump_config_);
    297 
    298   event_filters_.insert(event_filters_.end(), config.event_filters().begin(),
    299                         config.event_filters().end());
    300 }
    301 
    302 void TraceConfig::Clear() {
    303   record_mode_ = RECORD_UNTIL_FULL;
    304   enable_systrace_ = false;
    305   enable_argument_filter_ = false;
    306   category_filter_.Clear();
    307   memory_dump_config_.Clear();
    308   event_filters_.clear();
    309 }
    310 
    311 void TraceConfig::InitializeDefault() {
    312   record_mode_ = RECORD_UNTIL_FULL;
    313   enable_systrace_ = false;
    314   enable_argument_filter_ = false;
    315 }
    316 
    317 void TraceConfig::InitializeFromConfigDict(const DictionaryValue& dict) {
    318   record_mode_ = RECORD_UNTIL_FULL;
    319   std::string record_mode;
    320   if (dict.GetString(kRecordModeParam, &record_mode)) {
    321     if (record_mode == kRecordUntilFull) {
    322       record_mode_ = RECORD_UNTIL_FULL;
    323     } else if (record_mode == kRecordContinuously) {
    324       record_mode_ = RECORD_CONTINUOUSLY;
    325     } else if (record_mode == kTraceToConsole) {
    326       record_mode_ = ECHO_TO_CONSOLE;
    327     } else if (record_mode == kRecordAsMuchAsPossible) {
    328       record_mode_ = RECORD_AS_MUCH_AS_POSSIBLE;
    329     }
    330   }
    331 
    332   bool val;
    333   enable_systrace_ = dict.GetBoolean(kEnableSystraceParam, &val) ? val : false;
    334   enable_argument_filter_ =
    335       dict.GetBoolean(kEnableArgumentFilterParam, &val) ? val : false;
    336 
    337   category_filter_.InitializeFromConfigDict(dict);
    338 
    339   const base::ListValue* category_event_filters = nullptr;
    340   if (dict.GetList(kEventFiltersParam, &category_event_filters))
    341     SetEventFiltersFromConfigList(*category_event_filters);
    342 
    343   if (category_filter_.IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
    344     // If dump triggers not set, the client is using the legacy with just
    345     // category enabled. So, use the default periodic dump config.
    346     const DictionaryValue* memory_dump_config = nullptr;
    347     if (dict.GetDictionary(kMemoryDumpConfigParam, &memory_dump_config))
    348       SetMemoryDumpConfigFromConfigDict(*memory_dump_config);
    349     else
    350       SetDefaultMemoryDumpConfig();
    351   }
    352 }
    353 
    354 void TraceConfig::InitializeFromConfigString(StringPiece config_string) {
    355   auto dict = DictionaryValue::From(JSONReader::Read(config_string));
    356   if (dict)
    357     InitializeFromConfigDict(*dict);
    358   else
    359     InitializeDefault();
    360 }
    361 
    362 void TraceConfig::InitializeFromStrings(StringPiece category_filter_string,
    363                                         StringPiece trace_options_string) {
    364   if (!category_filter_string.empty())
    365     category_filter_.InitializeFromString(category_filter_string);
    366 
    367   record_mode_ = RECORD_UNTIL_FULL;
    368   enable_systrace_ = false;
    369   enable_argument_filter_ = false;
    370   if (!trace_options_string.empty()) {
    371     std::vector<std::string> split =
    372         SplitString(trace_options_string, ",", TRIM_WHITESPACE, SPLIT_WANT_ALL);
    373     for (const std::string& token : split) {
    374       if (token == kRecordUntilFull) {
    375         record_mode_ = RECORD_UNTIL_FULL;
    376       } else if (token == kRecordContinuously) {
    377         record_mode_ = RECORD_CONTINUOUSLY;
    378       } else if (token == kTraceToConsole) {
    379         record_mode_ = ECHO_TO_CONSOLE;
    380       } else if (token == kRecordAsMuchAsPossible) {
    381         record_mode_ = RECORD_AS_MUCH_AS_POSSIBLE;
    382       } else if (token == kEnableSystrace) {
    383         enable_systrace_ = true;
    384       } else if (token == kEnableArgumentFilter) {
    385         enable_argument_filter_ = true;
    386       }
    387     }
    388   }
    389 
    390   if (category_filter_.IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
    391     SetDefaultMemoryDumpConfig();
    392   }
    393 }
    394 
    395 void TraceConfig::SetMemoryDumpConfigFromConfigDict(
    396     const DictionaryValue& memory_dump_config) {
    397   // Set allowed dump modes.
    398   memory_dump_config_.allowed_dump_modes.clear();
    399   const ListValue* allowed_modes_list;
    400   if (memory_dump_config.GetList(kAllowedDumpModesParam, &allowed_modes_list)) {
    401     for (size_t i = 0; i < allowed_modes_list->GetSize(); ++i) {
    402       std::string level_of_detail_str;
    403       allowed_modes_list->GetString(i, &level_of_detail_str);
    404       memory_dump_config_.allowed_dump_modes.insert(
    405           StringToMemoryDumpLevelOfDetail(level_of_detail_str));
    406     }
    407   } else {
    408     // If allowed modes param is not given then allow all modes by default.
    409     memory_dump_config_.allowed_dump_modes = GetDefaultAllowedMemoryDumpModes();
    410   }
    411 
    412   // Set triggers
    413   memory_dump_config_.triggers.clear();
    414   const ListValue* trigger_list = nullptr;
    415   if (memory_dump_config.GetList(kTriggersParam, &trigger_list) &&
    416       trigger_list->GetSize() > 0) {
    417     for (size_t i = 0; i < trigger_list->GetSize(); ++i) {
    418       const DictionaryValue* trigger = nullptr;
    419       if (!trigger_list->GetDictionary(i, &trigger))
    420         continue;
    421 
    422       MemoryDumpConfig::Trigger dump_config;
    423       int interval = 0;
    424       if (!trigger->GetInteger(kMinTimeBetweenDumps, &interval)) {
    425         // If "min_time_between_dumps_ms" param was not given, then the trace
    426         // config uses old format where only periodic dumps are supported.
    427         trigger->GetInteger(kPeriodicIntervalLegacyParam, &interval);
    428         dump_config.trigger_type = MemoryDumpType::PERIODIC_INTERVAL;
    429       } else {
    430         std::string trigger_type_str;
    431         trigger->GetString(kTriggerTypeParam, &trigger_type_str);
    432         dump_config.trigger_type = StringToMemoryDumpType(trigger_type_str);
    433       }
    434       DCHECK_GT(interval, 0);
    435       dump_config.min_time_between_dumps_ms = static_cast<uint32_t>(interval);
    436 
    437       std::string level_of_detail_str;
    438       trigger->GetString(kTriggerModeParam, &level_of_detail_str);
    439       dump_config.level_of_detail =
    440           StringToMemoryDumpLevelOfDetail(level_of_detail_str);
    441 
    442       memory_dump_config_.triggers.push_back(dump_config);
    443     }
    444   }
    445 
    446   // Set heap profiler options
    447   const DictionaryValue* heap_profiler_options = nullptr;
    448   if (memory_dump_config.GetDictionary(kHeapProfilerOptions,
    449                                        &heap_profiler_options)) {
    450     int min_size_bytes = 0;
    451     if (heap_profiler_options->GetInteger(kBreakdownThresholdBytes,
    452                                          &min_size_bytes)
    453         && min_size_bytes >= 0) {
    454       memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes =
    455           static_cast<size_t>(min_size_bytes);
    456     } else {
    457       memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes =
    458           MemoryDumpConfig::HeapProfiler::kDefaultBreakdownThresholdBytes;
    459     }
    460   }
    461 }
    462 
    463 void TraceConfig::SetDefaultMemoryDumpConfig() {
    464   memory_dump_config_.Clear();
    465   memory_dump_config_.triggers.push_back(kDefaultHeavyMemoryDumpTrigger);
    466   memory_dump_config_.triggers.push_back(kDefaultLightMemoryDumpTrigger);
    467   memory_dump_config_.allowed_dump_modes = GetDefaultAllowedMemoryDumpModes();
    468 }
    469 
    470 void TraceConfig::SetEventFiltersFromConfigList(
    471     const base::ListValue& category_event_filters) {
    472   event_filters_.clear();
    473 
    474   for (size_t event_filter_index = 0;
    475        event_filter_index < category_event_filters.GetSize();
    476        ++event_filter_index) {
    477     const base::DictionaryValue* event_filter = nullptr;
    478     if (!category_event_filters.GetDictionary(event_filter_index,
    479                                               &event_filter))
    480       continue;
    481 
    482     std::string predicate_name;
    483     CHECK(event_filter->GetString(kFilterPredicateParam, &predicate_name))
    484         << "Invalid predicate name in category event filter.";
    485 
    486     EventFilterConfig new_config(predicate_name);
    487     new_config.InitializeFromConfigDict(event_filter);
    488     event_filters_.push_back(new_config);
    489   }
    490 }
    491 
    492 std::unique_ptr<DictionaryValue> TraceConfig::ToDict() const {
    493   auto dict = MakeUnique<DictionaryValue>();
    494   switch (record_mode_) {
    495     case RECORD_UNTIL_FULL:
    496       dict->SetString(kRecordModeParam, kRecordUntilFull);
    497       break;
    498     case RECORD_CONTINUOUSLY:
    499       dict->SetString(kRecordModeParam, kRecordContinuously);
    500       break;
    501     case RECORD_AS_MUCH_AS_POSSIBLE:
    502       dict->SetString(kRecordModeParam, kRecordAsMuchAsPossible);
    503       break;
    504     case ECHO_TO_CONSOLE:
    505       dict->SetString(kRecordModeParam, kTraceToConsole);
    506       break;
    507     default:
    508       NOTREACHED();
    509   }
    510 
    511   dict->SetBoolean(kEnableSystraceParam, enable_systrace_);
    512   dict->SetBoolean(kEnableArgumentFilterParam, enable_argument_filter_);
    513 
    514   category_filter_.ToDict(dict.get());
    515 
    516   if (!event_filters_.empty()) {
    517     std::unique_ptr<base::ListValue> filter_list(new base::ListValue());
    518     for (const EventFilterConfig& filter : event_filters_) {
    519       std::unique_ptr<base::DictionaryValue> filter_dict(
    520           new base::DictionaryValue());
    521       filter.ToDict(filter_dict.get());
    522       filter_list->Append(std::move(filter_dict));
    523     }
    524     dict->Set(kEventFiltersParam, std::move(filter_list));
    525   }
    526 
    527   if (category_filter_.IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
    528     auto allowed_modes = MakeUnique<ListValue>();
    529     for (auto dump_mode : memory_dump_config_.allowed_dump_modes)
    530       allowed_modes->AppendString(MemoryDumpLevelOfDetailToString(dump_mode));
    531 
    532     auto memory_dump_config = MakeUnique<DictionaryValue>();
    533     memory_dump_config->Set(kAllowedDumpModesParam, std::move(allowed_modes));
    534 
    535     auto triggers_list = MakeUnique<ListValue>();
    536     for (const auto& config : memory_dump_config_.triggers) {
    537       auto trigger_dict = MakeUnique<DictionaryValue>();
    538       trigger_dict->SetString(kTriggerTypeParam,
    539                               MemoryDumpTypeToString(config.trigger_type));
    540       trigger_dict->SetInteger(
    541           kMinTimeBetweenDumps,
    542           static_cast<int>(config.min_time_between_dumps_ms));
    543       trigger_dict->SetString(
    544           kTriggerModeParam,
    545           MemoryDumpLevelOfDetailToString(config.level_of_detail));
    546       triggers_list->Append(std::move(trigger_dict));
    547     }
    548 
    549     // Empty triggers will still be specified explicitly since it means that
    550     // the periodic dumps are not enabled.
    551     memory_dump_config->Set(kTriggersParam, std::move(triggers_list));
    552 
    553     if (memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes !=
    554         MemoryDumpConfig::HeapProfiler::kDefaultBreakdownThresholdBytes) {
    555       auto options = MakeUnique<DictionaryValue>();
    556       options->SetInteger(
    557           kBreakdownThresholdBytes,
    558           memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes);
    559       memory_dump_config->Set(kHeapProfilerOptions, std::move(options));
    560     }
    561     dict->Set(kMemoryDumpConfigParam, std::move(memory_dump_config));
    562   }
    563   return dict;
    564 }
    565 
    566 std::string TraceConfig::ToTraceOptionsString() const {
    567   std::string ret;
    568   switch (record_mode_) {
    569     case RECORD_UNTIL_FULL:
    570       ret = kRecordUntilFull;
    571       break;
    572     case RECORD_CONTINUOUSLY:
    573       ret = kRecordContinuously;
    574       break;
    575     case RECORD_AS_MUCH_AS_POSSIBLE:
    576       ret = kRecordAsMuchAsPossible;
    577       break;
    578     case ECHO_TO_CONSOLE:
    579       ret = kTraceToConsole;
    580       break;
    581     default:
    582       NOTREACHED();
    583   }
    584   if (enable_systrace_)
    585     ret = ret + "," + kEnableSystrace;
    586   if (enable_argument_filter_)
    587     ret = ret + "," + kEnableArgumentFilter;
    588   return ret;
    589 }
    590 
    591 }  // namespace trace_event
    592 }  // namespace base
    593