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
19namespace base {
20namespace trace_event {
21
22namespace {
23
24// String options that can be used to initialize TraceOptions.
25const char kRecordUntilFull[] = "record-until-full";
26const char kRecordContinuously[] = "record-continuously";
27const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible";
28const char kTraceToConsole[] = "trace-to-console";
29const char kEnableSystrace[] = "enable-systrace";
30const char kEnableArgumentFilter[] = "enable-argument-filter";
31
32// String parameters that can be used to parse the trace config string.
33const char kRecordModeParam[] = "record_mode";
34const char kEnableSystraceParam[] = "enable_systrace";
35const char kEnableArgumentFilterParam[] = "enable_argument_filter";
36
37// String parameters that is used to parse memory dump config in trace config
38// string.
39const char kMemoryDumpConfigParam[] = "memory_dump_config";
40const char kAllowedDumpModesParam[] = "allowed_dump_modes";
41const char kTriggersParam[] = "triggers";
42const char kTriggerModeParam[] = "mode";
43const char kMinTimeBetweenDumps[] = "min_time_between_dumps_ms";
44const char kTriggerTypeParam[] = "type";
45const char kPeriodicIntervalLegacyParam[] = "periodic_interval_ms";
46const char kHeapProfilerOptions[] = "heap_profiler_options";
47const char kBreakdownThresholdBytes[] = "breakdown_threshold_bytes";
48
49// String parameters used to parse category event filters.
50const char kEventFiltersParam[] = "event_filters";
51const char kFilterPredicateParam[] = "filter_predicate";
52const char kFilterArgsParam[] = "filter_args";
53
54// Default configuration of memory dumps.
55const TraceConfig::MemoryDumpConfig::Trigger kDefaultHeavyMemoryDumpTrigger = {
56    2000,  // min_time_between_dumps_ms
57    MemoryDumpLevelOfDetail::DETAILED, MemoryDumpType::PERIODIC_INTERVAL};
58const TraceConfig::MemoryDumpConfig::Trigger kDefaultLightMemoryDumpTrigger = {
59    250,  // min_time_between_dumps_ms
60    MemoryDumpLevelOfDetail::LIGHT, MemoryDumpType::PERIODIC_INTERVAL};
61
62class 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
78std::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
89TraceConfig::MemoryDumpConfig::HeapProfiler::HeapProfiler()
90    : breakdown_threshold_bytes(kDefaultBreakdownThresholdBytes) {}
91
92void TraceConfig::MemoryDumpConfig::HeapProfiler::Clear() {
93  breakdown_threshold_bytes = kDefaultBreakdownThresholdBytes;
94}
95
96void TraceConfig::ResetMemoryDumpConfig(
97    const TraceConfig::MemoryDumpConfig& memory_dump_config) {
98  memory_dump_config_.Clear();
99  memory_dump_config_ = memory_dump_config;
100}
101
102TraceConfig::MemoryDumpConfig::MemoryDumpConfig() {}
103
104TraceConfig::MemoryDumpConfig::MemoryDumpConfig(
105    const MemoryDumpConfig& other) = default;
106
107TraceConfig::MemoryDumpConfig::~MemoryDumpConfig() {}
108
109void TraceConfig::MemoryDumpConfig::Clear() {
110  allowed_dump_modes.clear();
111  triggers.clear();
112  heap_profiler_options.Clear();
113}
114
115void 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
126TraceConfig::EventFilterConfig::EventFilterConfig(
127    const std::string& predicate_name)
128    : predicate_name_(predicate_name) {}
129
130TraceConfig::EventFilterConfig::~EventFilterConfig() {}
131
132TraceConfig::EventFilterConfig::EventFilterConfig(const EventFilterConfig& tc) {
133  *this = tc;
134}
135
136TraceConfig::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
150void 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
159void TraceConfig::EventFilterConfig::SetCategoryFilter(
160    const TraceConfigCategoryFilter& category_filter) {
161  category_filter_ = category_filter;
162}
163
164void 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
174bool 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
188bool TraceConfig::EventFilterConfig::IsCategoryGroupEnabled(
189    const StringPiece& category_group_name) const {
190  return category_filter_.IsCategoryGroupEnabled(category_group_name);
191}
192
193TraceConfig::TraceConfig() {
194  InitializeDefault();
195}
196
197TraceConfig::TraceConfig(StringPiece category_filter_string,
198                         StringPiece trace_options_string) {
199  InitializeFromStrings(category_filter_string, trace_options_string);
200}
201
202TraceConfig::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
224TraceConfig::TraceConfig(const DictionaryValue& config) {
225  InitializeFromConfigDict(config);
226}
227
228TraceConfig::TraceConfig(StringPiece config_string) {
229  if (!config_string.empty())
230    InitializeFromConfigString(config_string);
231  else
232    InitializeDefault();
233}
234
235TraceConfig::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
243TraceConfig::~TraceConfig() {
244}
245
246TraceConfig& 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
259const TraceConfig::StringList& TraceConfig::GetSyntheticDelayValues() const {
260  return category_filter_.synthetic_delays();
261}
262
263std::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
270std::unique_ptr<ConvertableToTraceFormat>
271TraceConfig::AsConvertableToTraceFormat() const {
272  return MakeUnique<ConvertableTraceConfigToTraceFormat>(*this);
273}
274
275std::string TraceConfig::ToCategoryFilterString() const {
276  return category_filter_.ToFilterString();
277}
278
279bool 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
286void 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
302void 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
311void TraceConfig::InitializeDefault() {
312  record_mode_ = RECORD_UNTIL_FULL;
313  enable_systrace_ = false;
314  enable_argument_filter_ = false;
315}
316
317void 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
354void 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
362void 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
395void 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
463void 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
470void 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
492std::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
566std::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