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