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