1 // Copyright 2015 The Chromium Authors
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 <algorithm>
10 #include <utility>
11
12 #include "base/json/json_reader.h"
13 #include "base/json/json_writer.h"
14 #include "base/logging.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/notreached.h"
17 #include "base/strings/string_split.h"
18 #include "base/trace_event/memory_dump_manager.h"
19 #include "base/trace_event/memory_dump_request_args.h"
20 #include "base/trace_event/trace_event.h"
21 #include "third_party/abseil-cpp/absl/types/optional.h"
22
23 #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
24 #include "third_party/perfetto/protos/perfetto/config/track_event/track_event_config.gen.h" // nogncheck
25 #endif
26
27 namespace base::trace_event {
28
29 namespace {
30
31 // String options that can be used to initialize TraceOptions.
32 const char kRecordUntilFull[] = "record-until-full";
33 const char kRecordContinuously[] = "record-continuously";
34 const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible";
35 const char kTraceToConsole[] = "trace-to-console";
36 const char kEnableSystrace[] = "enable-systrace";
37 constexpr int kEnableSystraceLength = sizeof(kEnableSystrace) - 1;
38
39 const char kEnableArgumentFilter[] = "enable-argument-filter";
40
41 // String parameters that can be used to parse the trace config string.
42 const char kRecordModeParam[] = "record_mode";
43 const char kTraceBufferSizeInEvents[] = "trace_buffer_size_in_events";
44 const char kTraceBufferSizeInKb[] = "trace_buffer_size_in_kb";
45 const char kEnableSystraceParam[] = "enable_systrace";
46 const char kSystraceEventsParam[] = "enable_systrace_events";
47 const char kEnableArgumentFilterParam[] = "enable_argument_filter";
48 const char kEnableEventPackageNameFilterParam[] = "enable_package_name_filter";
49
50 // String parameters that is used to parse memory dump config in trace config
51 // string.
52 const char kMemoryDumpConfigParam[] = "memory_dump_config";
53 const char kAllowedDumpModesParam[] = "allowed_dump_modes";
54 const char kTriggersParam[] = "triggers";
55 const char kTriggerModeParam[] = "mode";
56 const char kMinTimeBetweenDumps[] = "min_time_between_dumps_ms";
57 const char kTriggerTypeParam[] = "type";
58 const char kPeriodicIntervalLegacyParam[] = "periodic_interval_ms";
59 const char kHeapProfilerOptions[] = "heap_profiler_options";
60 const char kBreakdownThresholdBytes[] = "breakdown_threshold_bytes";
61
62 // String parameters used to parse category event filters.
63 const char kEventFiltersParam[] = "event_filters";
64 const char kFilterPredicateParam[] = "filter_predicate";
65 const char kFilterArgsParam[] = "filter_args";
66
67 // String parameter used to parse process filter.
68 const char kIncludedProcessesParam[] = "included_process_ids";
69
70 const char kHistogramNamesParam[] = "histogram_names";
71
72 class ConvertableTraceConfigToTraceFormat
73 : public base::trace_event::ConvertableToTraceFormat {
74 public:
ConvertableTraceConfigToTraceFormat(const TraceConfig & trace_config)75 explicit ConvertableTraceConfigToTraceFormat(const TraceConfig& trace_config)
76 : trace_config_(trace_config) {}
77
78 ~ConvertableTraceConfigToTraceFormat() override = default;
79
AppendAsTraceFormat(std::string * out) const80 void AppendAsTraceFormat(std::string* out) const override {
81 out->append(trace_config_.ToString());
82 }
83
84 private:
85 const TraceConfig trace_config_;
86 };
87
GetDefaultAllowedMemoryDumpModes()88 std::set<MemoryDumpLevelOfDetail> GetDefaultAllowedMemoryDumpModes() {
89 std::set<MemoryDumpLevelOfDetail> all_modes;
90 for (uint32_t mode = static_cast<uint32_t>(MemoryDumpLevelOfDetail::FIRST);
91 mode <= static_cast<uint32_t>(MemoryDumpLevelOfDetail::LAST); mode++) {
92 all_modes.insert(static_cast<MemoryDumpLevelOfDetail>(mode));
93 }
94 return all_modes;
95 }
96
97 } // namespace
98
HeapProfiler()99 TraceConfig::MemoryDumpConfig::HeapProfiler::HeapProfiler()
100 : breakdown_threshold_bytes(kDefaultBreakdownThresholdBytes) {}
101
Clear()102 void TraceConfig::MemoryDumpConfig::HeapProfiler::Clear() {
103 breakdown_threshold_bytes = kDefaultBreakdownThresholdBytes;
104 }
105
ResetMemoryDumpConfig(const TraceConfig::MemoryDumpConfig & memory_dump_config)106 void TraceConfig::ResetMemoryDumpConfig(
107 const TraceConfig::MemoryDumpConfig& memory_dump_config) {
108 memory_dump_config_.Clear();
109 memory_dump_config_ = memory_dump_config;
110 }
111
112 TraceConfig::MemoryDumpConfig::MemoryDumpConfig() = default;
113
114 TraceConfig::MemoryDumpConfig::MemoryDumpConfig(
115 const MemoryDumpConfig& other) = default;
116
117 TraceConfig::MemoryDumpConfig::~MemoryDumpConfig() = default;
118
Clear()119 void TraceConfig::MemoryDumpConfig::Clear() {
120 allowed_dump_modes.clear();
121 triggers.clear();
122 heap_profiler_options.Clear();
123 }
124
Merge(const TraceConfig::MemoryDumpConfig & config)125 void TraceConfig::MemoryDumpConfig::Merge(
126 const TraceConfig::MemoryDumpConfig& config) {
127 triggers.insert(triggers.end(), config.triggers.begin(),
128 config.triggers.end());
129 allowed_dump_modes.insert(config.allowed_dump_modes.begin(),
130 config.allowed_dump_modes.end());
131 heap_profiler_options.breakdown_threshold_bytes =
132 std::min(heap_profiler_options.breakdown_threshold_bytes,
133 config.heap_profiler_options.breakdown_threshold_bytes);
134 }
135
136 TraceConfig::ProcessFilterConfig::ProcessFilterConfig() = default;
137
138 TraceConfig::ProcessFilterConfig::ProcessFilterConfig(
139 const ProcessFilterConfig& other) = default;
140
ProcessFilterConfig(const std::unordered_set<base::ProcessId> & included_process_ids)141 TraceConfig::ProcessFilterConfig::ProcessFilterConfig(
142 const std::unordered_set<base::ProcessId>& included_process_ids)
143 : included_process_ids_(included_process_ids) {}
144
145 TraceConfig::ProcessFilterConfig::~ProcessFilterConfig() = default;
146
Clear()147 void TraceConfig::ProcessFilterConfig::Clear() {
148 included_process_ids_.clear();
149 }
150
Merge(const ProcessFilterConfig & config)151 void TraceConfig::ProcessFilterConfig::Merge(
152 const ProcessFilterConfig& config) {
153 included_process_ids_.insert(config.included_process_ids_.begin(),
154 config.included_process_ids_.end());
155 }
156
InitializeFromConfigDict(const Value::Dict & dict)157 void TraceConfig::ProcessFilterConfig::InitializeFromConfigDict(
158 const Value::Dict& dict) {
159 included_process_ids_.clear();
160 const Value::List* value = dict.FindList(kIncludedProcessesParam);
161 if (!value)
162 return;
163 for (auto& pid_value : *value) {
164 if (pid_value.is_int()) {
165 included_process_ids_.insert(
166 static_cast<base::ProcessId>(pid_value.GetInt()));
167 }
168 }
169 }
170
ToDict(Value::Dict & dict) const171 void TraceConfig::ProcessFilterConfig::ToDict(Value::Dict& dict) const {
172 if (included_process_ids_.empty())
173 return;
174 base::Value::List list;
175 std::set<base::ProcessId> ordered_set(included_process_ids_.begin(),
176 included_process_ids_.end());
177 for (auto process_id : ordered_set)
178 list.Append(static_cast<int>(process_id));
179 dict.Set(kIncludedProcessesParam, std::move(list));
180 }
181
IsEnabled(base::ProcessId process_id) const182 bool TraceConfig::ProcessFilterConfig::IsEnabled(
183 base::ProcessId process_id) const {
184 return included_process_ids_.empty() ||
185 included_process_ids_.count(process_id);
186 }
187
EventFilterConfig(const std::string & predicate_name)188 TraceConfig::EventFilterConfig::EventFilterConfig(
189 const std::string& predicate_name)
190 : predicate_name_(predicate_name) {}
191
192 TraceConfig::EventFilterConfig::~EventFilterConfig() = default;
193
EventFilterConfig(const EventFilterConfig & tc)194 TraceConfig::EventFilterConfig::EventFilterConfig(const EventFilterConfig& tc) {
195 *this = tc;
196 }
197
operator =(const TraceConfig::EventFilterConfig & rhs)198 TraceConfig::EventFilterConfig& TraceConfig::EventFilterConfig::operator=(
199 const TraceConfig::EventFilterConfig& rhs) {
200 if (this == &rhs)
201 return *this;
202
203 predicate_name_ = rhs.predicate_name_;
204 category_filter_ = rhs.category_filter_;
205
206 args_ = rhs.args_.Clone();
207
208 return *this;
209 }
210
InitializeFromConfigDict(const Value::Dict & event_filter)211 void TraceConfig::EventFilterConfig::InitializeFromConfigDict(
212 const Value::Dict& event_filter) {
213 category_filter_.InitializeFromConfigDict(event_filter);
214
215 const Value::Dict* args_dict = event_filter.FindDict(kFilterArgsParam);
216 if (args_dict)
217 args_ = args_dict->Clone();
218 }
219
SetCategoryFilter(const TraceConfigCategoryFilter & category_filter)220 void TraceConfig::EventFilterConfig::SetCategoryFilter(
221 const TraceConfigCategoryFilter& category_filter) {
222 category_filter_ = category_filter;
223 }
224
ToDict(Value::Dict & filter_dict) const225 void TraceConfig::EventFilterConfig::ToDict(Value::Dict& filter_dict) const {
226 filter_dict.Set(kFilterPredicateParam, predicate_name());
227
228 category_filter_.ToDict(filter_dict);
229
230 if (!args_.empty()) {
231 filter_dict.Set(kFilterArgsParam, args_.Clone());
232 }
233 }
234
GetArgAsSet(const char * key,std::unordered_set<std::string> * out_set) const235 bool TraceConfig::EventFilterConfig::GetArgAsSet(
236 const char* key,
237 std::unordered_set<std::string>* out_set) const {
238 const Value::List* list = args_.FindList(key);
239 if (!list)
240 return false;
241 for (const Value& item : *list) {
242 if (item.is_string())
243 out_set->insert(item.GetString());
244 }
245 return true;
246 }
247
IsCategoryGroupEnabled(const StringPiece & category_group_name) const248 bool TraceConfig::EventFilterConfig::IsCategoryGroupEnabled(
249 const StringPiece& category_group_name) const {
250 return category_filter_.IsCategoryGroupEnabled(category_group_name);
251 }
252
253 // static
TraceRecordModeToStr(TraceRecordMode record_mode)254 std::string TraceConfig::TraceRecordModeToStr(TraceRecordMode record_mode) {
255 switch (record_mode) {
256 case RECORD_UNTIL_FULL:
257 return kRecordUntilFull;
258 case RECORD_CONTINUOUSLY:
259 return kRecordContinuously;
260 case RECORD_AS_MUCH_AS_POSSIBLE:
261 return kRecordAsMuchAsPossible;
262 case ECHO_TO_CONSOLE:
263 return kTraceToConsole;
264 }
265 return kRecordUntilFull;
266 }
267
TraceConfig()268 TraceConfig::TraceConfig() {
269 InitializeDefault();
270 }
271
TraceConfig(StringPiece category_filter_string,StringPiece trace_options_string)272 TraceConfig::TraceConfig(StringPiece category_filter_string,
273 StringPiece trace_options_string) {
274 InitializeFromStrings(category_filter_string, trace_options_string);
275 }
276
TraceConfig(StringPiece category_filter_string,TraceRecordMode record_mode)277 TraceConfig::TraceConfig(StringPiece category_filter_string,
278 TraceRecordMode record_mode) {
279 InitializeFromStrings(category_filter_string,
280 TraceConfig::TraceRecordModeToStr(record_mode));
281 }
282
TraceConfig(const Value::Dict & config)283 TraceConfig::TraceConfig(const Value::Dict& config) {
284 InitializeFromConfigDict(config);
285 }
286
TraceConfig(StringPiece config_string)287 TraceConfig::TraceConfig(StringPiece config_string) {
288 if (!config_string.empty())
289 InitializeFromConfigString(config_string);
290 else
291 InitializeDefault();
292 }
293
294 TraceConfig::TraceConfig(const TraceConfig& tc) = default;
295
296 TraceConfig::~TraceConfig() = default;
297
operator =(const TraceConfig & rhs)298 TraceConfig& TraceConfig::operator=(const TraceConfig& rhs) {
299 if (this == &rhs)
300 return *this;
301
302 record_mode_ = rhs.record_mode_;
303 trace_buffer_size_in_events_ = rhs.trace_buffer_size_in_events_;
304 trace_buffer_size_in_kb_ = rhs.trace_buffer_size_in_kb_;
305 enable_systrace_ = rhs.enable_systrace_;
306 enable_argument_filter_ = rhs.enable_argument_filter_;
307 category_filter_ = rhs.category_filter_;
308 process_filter_config_ = rhs.process_filter_config_;
309 enable_event_package_name_filter_ = rhs.enable_event_package_name_filter_;
310 memory_dump_config_ = rhs.memory_dump_config_;
311 event_filters_ = rhs.event_filters_;
312 histogram_names_ = rhs.histogram_names_;
313 systrace_events_ = rhs.systrace_events_;
314 return *this;
315 }
316
ToString() const317 std::string TraceConfig::ToString() const {
318 Value dict = ToValue();
319 std::string json;
320 JSONWriter::Write(dict, &json);
321 return json;
322 }
323
324 std::unique_ptr<ConvertableToTraceFormat>
AsConvertableToTraceFormat() const325 TraceConfig::AsConvertableToTraceFormat() const {
326 return std::make_unique<ConvertableTraceConfigToTraceFormat>(*this);
327 }
328
ToCategoryFilterString() const329 std::string TraceConfig::ToCategoryFilterString() const {
330 return category_filter_.ToFilterString();
331 }
332
IsCategoryGroupEnabled(const StringPiece & category_group_name) const333 bool TraceConfig::IsCategoryGroupEnabled(
334 const StringPiece& category_group_name) const {
335 // TraceLog should call this method only as part of enabling/disabling
336 // categories.
337 return category_filter_.IsCategoryGroupEnabled(category_group_name);
338 }
339
Merge(const TraceConfig & config)340 void TraceConfig::Merge(const TraceConfig& config) {
341 if (record_mode_ != config.record_mode_ ||
342 enable_systrace_ != config.enable_systrace_ ||
343 enable_argument_filter_ != config.enable_argument_filter_ ||
344 enable_event_package_name_filter_ !=
345 config.enable_event_package_name_filter_) {
346 DLOG(ERROR) << "Attempting to merge trace config with a different "
347 << "set of options.";
348 }
349 DCHECK_EQ(trace_buffer_size_in_events_, config.trace_buffer_size_in_events_)
350 << "Cannot change trace buffer size";
351
352 category_filter_.Merge(config.category_filter_);
353 memory_dump_config_.Merge(config.memory_dump_config_);
354 process_filter_config_.Merge(config.process_filter_config_);
355
356 event_filters_.insert(event_filters_.end(), config.event_filters().begin(),
357 config.event_filters().end());
358 histogram_names_.insert(config.histogram_names().begin(),
359 config.histogram_names().end());
360 }
361
Clear()362 void TraceConfig::Clear() {
363 record_mode_ = RECORD_UNTIL_FULL;
364 trace_buffer_size_in_events_ = 0;
365 trace_buffer_size_in_kb_ = 0;
366 enable_systrace_ = false;
367 enable_argument_filter_ = false;
368 enable_event_package_name_filter_ = false;
369 category_filter_.Clear();
370 memory_dump_config_.Clear();
371 process_filter_config_.Clear();
372 event_filters_.clear();
373 histogram_names_.clear();
374 systrace_events_.clear();
375 }
376
InitializeDefault()377 void TraceConfig::InitializeDefault() {
378 record_mode_ = RECORD_UNTIL_FULL;
379 trace_buffer_size_in_events_ = 0;
380 trace_buffer_size_in_kb_ = 0;
381 enable_systrace_ = false;
382 enable_argument_filter_ = false;
383 enable_event_package_name_filter_ = false;
384 }
385
InitializeFromConfigDict(const Value::Dict & dict)386 void TraceConfig::InitializeFromConfigDict(const Value::Dict& dict) {
387 record_mode_ = RECORD_UNTIL_FULL;
388 const std::string* record_mode = dict.FindString(kRecordModeParam);
389 if (record_mode) {
390 if (*record_mode == kRecordUntilFull) {
391 record_mode_ = RECORD_UNTIL_FULL;
392 } else if (*record_mode == kRecordContinuously) {
393 record_mode_ = RECORD_CONTINUOUSLY;
394 } else if (*record_mode == kTraceToConsole) {
395 record_mode_ = ECHO_TO_CONSOLE;
396 } else if (*record_mode == kRecordAsMuchAsPossible) {
397 record_mode_ = RECORD_AS_MUCH_AS_POSSIBLE;
398 }
399 }
400 trace_buffer_size_in_events_ = base::saturated_cast<size_t>(
401 dict.FindInt(kTraceBufferSizeInEvents).value_or(0));
402 trace_buffer_size_in_kb_ = base::saturated_cast<size_t>(
403 dict.FindInt(kTraceBufferSizeInKb).value_or(0));
404
405 enable_systrace_ = dict.FindBool(kEnableSystraceParam).value_or(false);
406 enable_argument_filter_ =
407 dict.FindBool(kEnableArgumentFilterParam).value_or(false);
408 enable_event_package_name_filter_ =
409 dict.FindBool(kEnableEventPackageNameFilterParam).value_or(false);
410
411 category_filter_.InitializeFromConfigDict(dict);
412 process_filter_config_.InitializeFromConfigDict(dict);
413
414 const Value::List* category_event_filters = dict.FindList(kEventFiltersParam);
415 if (category_event_filters)
416 SetEventFiltersFromConfigList(*category_event_filters);
417 const Value::List* histogram_names = dict.FindList(kHistogramNamesParam);
418 if (histogram_names)
419 SetHistogramNamesFromConfigList(*histogram_names);
420
421 if (category_filter_.IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
422 // If dump triggers not set, the client is using the legacy with just
423 // category enabled. So, use the default periodic dump config.
424 const Value::Dict* memory_dump_config =
425 dict.FindDict(kMemoryDumpConfigParam);
426 if (memory_dump_config)
427 SetMemoryDumpConfigFromConfigDict(*memory_dump_config);
428 else
429 SetDefaultMemoryDumpConfig();
430 }
431
432 systrace_events_.clear();
433 if (enable_systrace_) {
434 const Value::List* systrace_events = dict.FindList(kSystraceEventsParam);
435 if (systrace_events) {
436 for (const Value& value : *systrace_events) {
437 systrace_events_.insert(value.GetString());
438 }
439 }
440 }
441 }
442
InitializeFromConfigString(StringPiece config_string)443 void TraceConfig::InitializeFromConfigString(StringPiece config_string) {
444 absl::optional<Value> dict = JSONReader::Read(config_string);
445 if (dict && dict->is_dict())
446 InitializeFromConfigDict(dict->GetDict());
447 else
448 InitializeDefault();
449 }
450
InitializeFromStrings(StringPiece category_filter_string,StringPiece trace_options_string)451 void TraceConfig::InitializeFromStrings(StringPiece category_filter_string,
452 StringPiece trace_options_string) {
453 if (!category_filter_string.empty())
454 category_filter_.InitializeFromString(category_filter_string);
455
456 record_mode_ = RECORD_UNTIL_FULL;
457 trace_buffer_size_in_events_ = 0;
458 trace_buffer_size_in_kb_ = 0;
459 enable_systrace_ = false;
460 systrace_events_.clear();
461 enable_argument_filter_ = false;
462 enable_event_package_name_filter_ = false;
463 if (!trace_options_string.empty()) {
464 std::vector<std::string> split =
465 SplitString(trace_options_string, ",", TRIM_WHITESPACE, SPLIT_WANT_ALL);
466 for (const std::string& token : split) {
467 if (token == kRecordUntilFull) {
468 record_mode_ = RECORD_UNTIL_FULL;
469 } else if (token == kRecordContinuously) {
470 record_mode_ = RECORD_CONTINUOUSLY;
471 } else if (token == kTraceToConsole) {
472 record_mode_ = ECHO_TO_CONSOLE;
473 } else if (token == kRecordAsMuchAsPossible) {
474 record_mode_ = RECORD_AS_MUCH_AS_POSSIBLE;
475 } else if (token.find(kEnableSystrace) == 0) {
476 // Find optional events list.
477 const size_t length = token.length();
478 if (length == kEnableSystraceLength) {
479 // Use all predefined categories.
480 enable_systrace_ = true;
481 continue;
482 }
483 const auto system_events_not_trimmed =
484 token.substr(kEnableSystraceLength);
485 const auto system_events =
486 TrimString(system_events_not_trimmed, kWhitespaceASCII, TRIM_ALL);
487 if (system_events[0] != '=') {
488 LOG(ERROR) << "Failed to parse " << token;
489 continue;
490 }
491 enable_systrace_ = true;
492 const std::vector<std::string> split_systrace_events = SplitString(
493 system_events.substr(1), " ", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
494 for (const std::string& systrace_event : split_systrace_events)
495 systrace_events_.insert(systrace_event);
496 } else if (token == kEnableArgumentFilter) {
497 enable_argument_filter_ = true;
498 }
499 }
500 }
501
502 if (category_filter_.IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
503 SetDefaultMemoryDumpConfig();
504 }
505 }
506
SetMemoryDumpConfigFromConfigDict(const Value::Dict & memory_dump_config)507 void TraceConfig::SetMemoryDumpConfigFromConfigDict(
508 const Value::Dict& memory_dump_config) {
509 // Set allowed dump modes.
510 memory_dump_config_.allowed_dump_modes.clear();
511 const Value::List* allowed_modes_list =
512 memory_dump_config.FindList(kAllowedDumpModesParam);
513 if (allowed_modes_list) {
514 for (const Value& item : *allowed_modes_list) {
515 DCHECK(item.is_string());
516 memory_dump_config_.allowed_dump_modes.insert(
517 StringToMemoryDumpLevelOfDetail(item.GetString()));
518 }
519 } else {
520 // If allowed modes param is not given then allow all modes by default.
521 memory_dump_config_.allowed_dump_modes = GetDefaultAllowedMemoryDumpModes();
522 }
523
524 // Set triggers
525 memory_dump_config_.triggers.clear();
526 const Value::List* trigger_list = memory_dump_config.FindList(kTriggersParam);
527 if (trigger_list) {
528 for (const Value& trigger : *trigger_list) {
529 if (!trigger.is_dict()) {
530 continue;
531 }
532 const Value::Dict& trigger_dict = trigger.GetDict();
533
534 MemoryDumpConfig::Trigger dump_config;
535 absl::optional<int> interval = trigger_dict.FindInt(kMinTimeBetweenDumps);
536 if (!interval) {
537 // If "min_time_between_dumps_ms" param was not given, then the trace
538 // config uses old format where only periodic dumps are supported.
539 interval = trigger_dict.FindInt(kPeriodicIntervalLegacyParam);
540 dump_config.trigger_type = MemoryDumpType::PERIODIC_INTERVAL;
541 } else {
542 const std::string* trigger_type_str =
543 trigger_dict.FindString(kTriggerTypeParam);
544 DCHECK(trigger_type_str);
545 dump_config.trigger_type = StringToMemoryDumpType(*trigger_type_str);
546 }
547 DCHECK(interval.has_value());
548 DCHECK_GT(*interval, 0);
549 dump_config.min_time_between_dumps_ms = static_cast<uint32_t>(*interval);
550
551 const std::string* level_of_detail_str =
552 trigger_dict.FindString(kTriggerModeParam);
553 DCHECK(level_of_detail_str);
554 dump_config.level_of_detail =
555 StringToMemoryDumpLevelOfDetail(*level_of_detail_str);
556
557 memory_dump_config_.triggers.push_back(dump_config);
558 }
559 }
560
561 // Set heap profiler options.
562 const Value::Dict* heap_profiler_options =
563 memory_dump_config.FindDict(kHeapProfilerOptions);
564 if (heap_profiler_options) {
565 absl::optional<int> min_size_bytes =
566 heap_profiler_options->FindInt(kBreakdownThresholdBytes);
567 if (min_size_bytes && *min_size_bytes >= 0) {
568 memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes =
569 static_cast<uint32_t>(*min_size_bytes);
570 } else {
571 memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes =
572 MemoryDumpConfig::HeapProfiler::kDefaultBreakdownThresholdBytes;
573 }
574 }
575 }
576
SetDefaultMemoryDumpConfig()577 void TraceConfig::SetDefaultMemoryDumpConfig() {
578 memory_dump_config_.Clear();
579 memory_dump_config_.allowed_dump_modes = GetDefaultAllowedMemoryDumpModes();
580 }
581
SetProcessFilterConfig(const ProcessFilterConfig & config)582 void TraceConfig::SetProcessFilterConfig(const ProcessFilterConfig& config) {
583 process_filter_config_ = config;
584 }
585
SetHistogramNamesFromConfigList(const Value::List & histogram_names)586 void TraceConfig::SetHistogramNamesFromConfigList(
587 const Value::List& histogram_names) {
588 histogram_names_.clear();
589 for (const Value& value : histogram_names) {
590 histogram_names_.insert(value.GetString());
591 }
592 }
593
SetEventFiltersFromConfigList(const Value::List & category_event_filters)594 void TraceConfig::SetEventFiltersFromConfigList(
595 const Value::List& category_event_filters) {
596 event_filters_.clear();
597
598 for (const Value& event_filter : category_event_filters) {
599 if (!event_filter.is_dict()) {
600 continue;
601 }
602 const Value::Dict& event_filter_dict = event_filter.GetDict();
603
604 const std::string* predicate_name =
605 event_filter_dict.FindString(kFilterPredicateParam);
606 CHECK(predicate_name) << "Invalid predicate name in category event filter.";
607
608 EventFilterConfig new_config(*predicate_name);
609 new_config.InitializeFromConfigDict(event_filter_dict);
610 event_filters_.push_back(new_config);
611 }
612 }
613
ToValue() const614 Value TraceConfig::ToValue() const {
615 Value::Dict dict;
616 dict.Set(kRecordModeParam, TraceConfig::TraceRecordModeToStr(record_mode_));
617 dict.Set(kEnableSystraceParam, enable_systrace_);
618 dict.Set(kEnableArgumentFilterParam, enable_argument_filter_);
619 if (trace_buffer_size_in_events_ > 0) {
620 dict.Set(kTraceBufferSizeInEvents,
621 base::checked_cast<int>(trace_buffer_size_in_events_));
622 }
623 if (trace_buffer_size_in_kb_ > 0) {
624 dict.Set(kTraceBufferSizeInKb,
625 base::checked_cast<int>(trace_buffer_size_in_kb_));
626 }
627
628 dict.Set(kEnableEventPackageNameFilterParam,
629 enable_event_package_name_filter_);
630
631 category_filter_.ToDict(dict);
632 process_filter_config_.ToDict(dict);
633
634 if (!event_filters_.empty()) {
635 Value::List filter_list;
636 for (const EventFilterConfig& filter : event_filters_) {
637 Value::Dict filter_dict;
638 filter.ToDict(filter_dict);
639 filter_list.Append(std::move(filter_dict));
640 }
641 dict.Set(kEventFiltersParam, std::move(filter_list));
642 }
643
644 if (category_filter_.IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
645 Value::List allowed_modes;
646 for (auto dump_mode : memory_dump_config_.allowed_dump_modes)
647 allowed_modes.Append(MemoryDumpLevelOfDetailToString(dump_mode));
648
649 Value::Dict memory_dump_config;
650 memory_dump_config.Set(kAllowedDumpModesParam, std::move(allowed_modes));
651
652 Value::List triggers_list;
653 for (const auto& config : memory_dump_config_.triggers) {
654 Value::Dict trigger_dict;
655
656 trigger_dict.Set(kTriggerTypeParam,
657 MemoryDumpTypeToString(config.trigger_type));
658 trigger_dict.Set(kMinTimeBetweenDumps,
659 static_cast<int>(config.min_time_between_dumps_ms));
660 trigger_dict.Set(kTriggerModeParam,
661 MemoryDumpLevelOfDetailToString(config.level_of_detail));
662 triggers_list.Append(std::move(trigger_dict));
663 }
664
665 // Empty triggers will still be specified explicitly since it means that
666 // the periodic dumps are not enabled.
667 memory_dump_config.Set(kTriggersParam, std::move(triggers_list));
668
669 if (memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes !=
670 MemoryDumpConfig::HeapProfiler::kDefaultBreakdownThresholdBytes) {
671 Value::Dict options;
672 options.Set(
673 kBreakdownThresholdBytes,
674 base::checked_cast<int>(memory_dump_config_.heap_profiler_options
675 .breakdown_threshold_bytes));
676 memory_dump_config.Set(kHeapProfilerOptions, std::move(options));
677 }
678 dict.Set(kMemoryDumpConfigParam, std::move(memory_dump_config));
679 }
680
681 if (!histogram_names_.empty()) {
682 base::Value::List histogram_names;
683 for (const std::string& histogram_name : histogram_names_)
684 histogram_names.Append(histogram_name);
685 dict.Set(kHistogramNamesParam, std::move(histogram_names));
686 }
687
688 if (enable_systrace_) {
689 if (!systrace_events_.empty()) {
690 base::Value::List systrace_events;
691 for (const std::string& systrace_event : systrace_events_)
692 systrace_events.Append(systrace_event);
693 dict.Set(kSystraceEventsParam, std::move(systrace_events));
694 }
695 }
696
697 return Value(std::move(dict));
698 }
699
EnableSystraceEvent(const std::string & systrace_event)700 void TraceConfig::EnableSystraceEvent(const std::string& systrace_event) {
701 systrace_events_.insert(systrace_event);
702 }
703
EnableHistogram(const std::string & histogram_name)704 void TraceConfig::EnableHistogram(const std::string& histogram_name) {
705 histogram_names_.insert(histogram_name);
706 }
707
ToTraceOptionsString() const708 std::string TraceConfig::ToTraceOptionsString() const {
709 std::string ret;
710 switch (record_mode_) {
711 case RECORD_UNTIL_FULL:
712 ret = kRecordUntilFull;
713 break;
714 case RECORD_CONTINUOUSLY:
715 ret = kRecordContinuously;
716 break;
717 case RECORD_AS_MUCH_AS_POSSIBLE:
718 ret = kRecordAsMuchAsPossible;
719 break;
720 case ECHO_TO_CONSOLE:
721 ret = kTraceToConsole;
722 break;
723 }
724 if (enable_systrace_) {
725 ret += ",";
726 ret += kEnableSystrace;
727 bool first_param = true;
728 for (const std::string& systrace_event : systrace_events_) {
729 if (first_param) {
730 ret += "=";
731 first_param = false;
732 } else {
733 ret += " ";
734 }
735 ret = ret + systrace_event;
736 }
737 }
738 if (enable_argument_filter_) {
739 ret += ",";
740 ret += kEnableArgumentFilter;
741 }
742 return ret;
743 }
744
745 #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
ToPerfettoTrackEventConfigRaw(bool privacy_filtering_enabled) const746 std::string TraceConfig::ToPerfettoTrackEventConfigRaw(
747 bool privacy_filtering_enabled) const {
748 perfetto::protos::gen::TrackEventConfig te_cfg;
749 // If no categories are explicitly enabled, enable the default ones.
750 // Otherwise only matching categories are enabled.
751 if (!category_filter_.included_categories().empty())
752 te_cfg.add_disabled_categories("*");
753 // Metadata is always enabled.
754 te_cfg.add_enabled_categories("__metadata");
755 for (const auto& excluded : category_filter_.excluded_categories()) {
756 te_cfg.add_disabled_categories(excluded);
757 }
758 for (const auto& included : category_filter_.included_categories()) {
759 te_cfg.add_enabled_categories(included);
760 }
761 for (const auto& disabled : category_filter_.disabled_categories()) {
762 te_cfg.add_enabled_categories(disabled);
763 }
764 te_cfg.set_enable_thread_time_sampling(true);
765 te_cfg.set_timestamp_unit_multiplier(1000);
766 if (privacy_filtering_enabled) {
767 te_cfg.set_filter_dynamic_event_names(true);
768 te_cfg.set_filter_debug_annotations(true);
769 }
770 return te_cfg.SerializeAsString();
771 }
772 #endif
773
774 } // namespace base::trace_event
775