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