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
23 namespace base {
24 namespace trace_event {
25
26 namespace {
27
28 // String options that can be used to initialize TraceOptions.
29 const char kRecordUntilFull[] = "record-until-full";
30 const char kRecordContinuously[] = "record-continuously";
31 const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible";
32 const char kTraceToConsole[] = "trace-to-console";
33 const char kEnableSampling[] = "enable-sampling";
34 const char kEnableSystrace[] = "enable-systrace";
35 const char kEnableArgumentFilter[] = "enable-argument-filter";
36
37 // String parameters that can be used to parse the trace config string.
38 const char kRecordModeParam[] = "record_mode";
39 const char kEnableSamplingParam[] = "enable_sampling";
40 const char kEnableSystraceParam[] = "enable_systrace";
41 const char kEnableArgumentFilterParam[] = "enable_argument_filter";
42 const char kIncludedCategoriesParam[] = "included_categories";
43 const char kExcludedCategoriesParam[] = "excluded_categories";
44 const char kSyntheticDelaysParam[] = "synthetic_delays";
45
46 const char kSyntheticDelayCategoryFilterPrefix[] = "DELAY(";
47
48 // String parameters that is used to parse memory dump config in trace config
49 // string.
50 const char kMemoryDumpConfigParam[] = "memory_dump_config";
51 const char kAllowedDumpModesParam[] = "allowed_dump_modes";
52 const char kTriggersParam[] = "triggers";
53 const char kPeriodicIntervalParam[] = "periodic_interval_ms";
54 const char kModeParam[] = "mode";
55 const char kHeapProfilerOptions[] = "heap_profiler_options";
56 const char kBreakdownThresholdBytes[] = "breakdown_threshold_bytes";
57
58 // Default configuration of memory dumps.
59 const TraceConfig::MemoryDumpConfig::Trigger kDefaultHeavyMemoryDumpTrigger = {
60 2000, // periodic_interval_ms
61 MemoryDumpLevelOfDetail::DETAILED};
62 const TraceConfig::MemoryDumpConfig::Trigger kDefaultLightMemoryDumpTrigger = {
63 250, // periodic_interval_ms
64 MemoryDumpLevelOfDetail::LIGHT};
65
66 class ConvertableTraceConfigToTraceFormat
67 : public base::trace_event::ConvertableToTraceFormat {
68 public:
ConvertableTraceConfigToTraceFormat(const TraceConfig & trace_config)69 explicit ConvertableTraceConfigToTraceFormat(const TraceConfig& trace_config)
70 : trace_config_(trace_config) {}
~ConvertableTraceConfigToTraceFormat()71 ~ConvertableTraceConfigToTraceFormat() override {}
72
AppendAsTraceFormat(std::string * out) const73 void AppendAsTraceFormat(std::string* out) const override {
74 out->append(trace_config_.ToString());
75 }
76
77 private:
78 const TraceConfig trace_config_;
79 };
80
GetDefaultAllowedMemoryDumpModes()81 std::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
HeapProfiler()92 TraceConfig::MemoryDumpConfig::HeapProfiler::HeapProfiler()
93 : breakdown_threshold_bytes(kDefaultBreakdownThresholdBytes) {}
94
Clear()95 void TraceConfig::MemoryDumpConfig::HeapProfiler::Clear() {
96 breakdown_threshold_bytes = kDefaultBreakdownThresholdBytes;
97 }
98
ResetMemoryDumpConfig(const TraceConfig::MemoryDumpConfig & memory_dump_config)99 void TraceConfig::ResetMemoryDumpConfig(
100 const TraceConfig::MemoryDumpConfig& memory_dump_config) {
101 memory_dump_config_.Clear();
102 memory_dump_config_ = memory_dump_config;
103 }
104
MemoryDumpConfig()105 TraceConfig::MemoryDumpConfig::MemoryDumpConfig() {}
106
107 TraceConfig::MemoryDumpConfig::MemoryDumpConfig(
108 const MemoryDumpConfig& other) = default;
109
~MemoryDumpConfig()110 TraceConfig::MemoryDumpConfig::~MemoryDumpConfig() {}
111
Clear()112 void TraceConfig::MemoryDumpConfig::Clear() {
113 allowed_dump_modes.clear();
114 triggers.clear();
115 heap_profiler_options.Clear();
116 }
117
TraceConfig()118 TraceConfig::TraceConfig() {
119 InitializeDefault();
120 }
121
TraceConfig(StringPiece category_filter_string,StringPiece trace_options_string)122 TraceConfig::TraceConfig(StringPiece category_filter_string,
123 StringPiece trace_options_string) {
124 InitializeFromStrings(category_filter_string, trace_options_string);
125 }
126
TraceConfig(StringPiece category_filter_string,TraceRecordMode record_mode)127 TraceConfig::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
TraceConfig(const DictionaryValue & config)149 TraceConfig::TraceConfig(const DictionaryValue& config) {
150 InitializeFromConfigDict(config);
151 }
152
TraceConfig(StringPiece config_string)153 TraceConfig::TraceConfig(StringPiece config_string) {
154 if (!config_string.empty())
155 InitializeFromConfigString(config_string);
156 else
157 InitializeDefault();
158 }
159
TraceConfig(const TraceConfig & tc)160 TraceConfig::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
~TraceConfig()171 TraceConfig::~TraceConfig() {
172 }
173
operator =(const TraceConfig & rhs)174 TraceConfig& 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
GetSyntheticDelayValues() const190 const TraceConfig::StringList& TraceConfig::GetSyntheticDelayValues() const {
191 return synthetic_delays_;
192 }
193
ToString() const194 std::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
201 std::unique_ptr<ConvertableToTraceFormat>
AsConvertableToTraceFormat() const202 TraceConfig::AsConvertableToTraceFormat() const {
203 return WrapUnique(new ConvertableTraceConfigToTraceFormat(*this));
204 }
205
ToCategoryFilterString() const206 std::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
IsCategoryGroupEnabled(const char * category_group_name) const215 bool 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
Merge(const TraceConfig & config)272 void 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
Clear()307 void 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
InitializeDefault()319 void TraceConfig::InitializeDefault() {
320 record_mode_ = RECORD_UNTIL_FULL;
321 enable_sampling_ = false;
322 enable_systrace_ = false;
323 enable_argument_filter_ = false;
324 }
325
InitializeFromConfigDict(const DictionaryValue & dict)326 void 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
InitializeFromConfigString(StringPiece config_string)366 void 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
InitializeFromStrings(StringPiece category_filter_string,StringPiece trace_options_string)374 void 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
SetCategoriesFromIncludedList(const ListValue & included_list)439 void 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
SetCategoriesFromExcludedList(const ListValue & excluded_list)455 void 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
SetSyntheticDelaysFromList(const ListValue & list)465 void 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
AddCategoryToDict(DictionaryValue * dict,const char * param,const StringList & categories) const480 void 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
SetMemoryDumpConfigFromConfigDict(const DictionaryValue & memory_dump_config)492 void 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
SetDefaultMemoryDumpConfig()551 void 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
ToDict() const558 std::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
ToTraceOptionsString() const624 std::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
WriteCategoryFilterString(const StringList & values,std::string * out,bool included) const651 void 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
WriteCategoryFilterString(const StringList & delays,std::string * out) const664 void 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
IsCategoryEnabled(const char * category_name) const677 bool 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
IsEmptyOrContainsLeadingOrTrailingWhitespace(StringPiece str)696 bool TraceConfig::IsEmptyOrContainsLeadingOrTrailingWhitespace(
697 StringPiece str) {
698 return str.empty() || str.front() == ' ' || str.back() == ' ';
699 }
700
HasIncludedPatterns() const701 bool TraceConfig::HasIncludedPatterns() const {
702 return !included_categories_.empty();
703 }
704
705 } // namespace trace_event
706 } // namespace base
707