1 // Copyright 2017 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_category_filter.h"
6
7 #include "base/memory/ptr_util.h"
8 #include "base/strings/pattern.h"
9 #include "base/strings/string_split.h"
10 #include "base/strings/string_tokenizer.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/trace_event/trace_event.h"
14
15 namespace base {
16 namespace trace_event {
17
18 namespace {
19 const char kIncludedCategoriesParam[] = "included_categories";
20 const char kExcludedCategoriesParam[] = "excluded_categories";
21 const char kSyntheticDelaysParam[] = "synthetic_delays";
22
23 const char kSyntheticDelayCategoryFilterPrefix[] = "DELAY(";
24 }
25
TraceConfigCategoryFilter()26 TraceConfigCategoryFilter::TraceConfigCategoryFilter() {}
27
TraceConfigCategoryFilter(const TraceConfigCategoryFilter & other)28 TraceConfigCategoryFilter::TraceConfigCategoryFilter(
29 const TraceConfigCategoryFilter& other)
30 : included_categories_(other.included_categories_),
31 disabled_categories_(other.disabled_categories_),
32 excluded_categories_(other.excluded_categories_),
33 synthetic_delays_(other.synthetic_delays_) {}
34
~TraceConfigCategoryFilter()35 TraceConfigCategoryFilter::~TraceConfigCategoryFilter() {}
36
operator =(const TraceConfigCategoryFilter & rhs)37 TraceConfigCategoryFilter& TraceConfigCategoryFilter::operator=(
38 const TraceConfigCategoryFilter& rhs) {
39 included_categories_ = rhs.included_categories_;
40 disabled_categories_ = rhs.disabled_categories_;
41 excluded_categories_ = rhs.excluded_categories_;
42 synthetic_delays_ = rhs.synthetic_delays_;
43 return *this;
44 }
45
InitializeFromString(const StringPiece & category_filter_string)46 void TraceConfigCategoryFilter::InitializeFromString(
47 const StringPiece& category_filter_string) {
48 std::vector<StringPiece> split = SplitStringPiece(
49 category_filter_string, ",", TRIM_WHITESPACE, SPLIT_WANT_ALL);
50 for (const StringPiece& category : split) {
51 // Ignore empty categories.
52 if (category.empty())
53 continue;
54 // Synthetic delays are of the form 'DELAY(delay;option;option;...)'.
55 if (StartsWith(category, kSyntheticDelayCategoryFilterPrefix,
56 CompareCase::SENSITIVE) &&
57 category.back() == ')') {
58 StringPiece synthetic_category = category.substr(
59 strlen(kSyntheticDelayCategoryFilterPrefix),
60 category.size() - strlen(kSyntheticDelayCategoryFilterPrefix) - 1);
61 size_t name_length = synthetic_category.find(';');
62 if (name_length != std::string::npos && name_length > 0 &&
63 name_length != synthetic_category.size() - 1) {
64 synthetic_delays_.push_back(synthetic_category.as_string());
65 }
66 } else if (category.front() == '-') {
67 // Excluded categories start with '-'.
68 // Remove '-' from category string.
69 excluded_categories_.push_back(category.substr(1).as_string());
70 } else if (category.starts_with(TRACE_DISABLED_BY_DEFAULT(""))) {
71 disabled_categories_.push_back(category.as_string());
72 } else {
73 included_categories_.push_back(category.as_string());
74 }
75 }
76 }
77
InitializeFromConfigDict(const DictionaryValue & dict)78 void TraceConfigCategoryFilter::InitializeFromConfigDict(
79 const DictionaryValue& dict) {
80 const ListValue* category_list = nullptr;
81 if (dict.GetList(kIncludedCategoriesParam, &category_list))
82 SetCategoriesFromIncludedList(*category_list);
83 if (dict.GetList(kExcludedCategoriesParam, &category_list))
84 SetCategoriesFromExcludedList(*category_list);
85 if (dict.GetList(kSyntheticDelaysParam, &category_list))
86 SetSyntheticDelaysFromList(*category_list);
87 }
88
IsCategoryGroupEnabled(const StringPiece & category_group_name) const89 bool TraceConfigCategoryFilter::IsCategoryGroupEnabled(
90 const StringPiece& category_group_name) const {
91 bool had_enabled_by_default = false;
92 DCHECK(!category_group_name.empty());
93 CStringTokenizer category_group_tokens(category_group_name.begin(),
94 category_group_name.end(), ",");
95 while (category_group_tokens.GetNext()) {
96 StringPiece category_group_token = category_group_tokens.token_piece();
97 // Don't allow empty tokens, nor tokens with leading or trailing space.
98 DCHECK(IsCategoryNameAllowed(category_group_token))
99 << "Disallowed category string";
100 if (IsCategoryEnabled(category_group_token))
101 return true;
102
103 if (!MatchPattern(category_group_token, TRACE_DISABLED_BY_DEFAULT("*")))
104 had_enabled_by_default = true;
105 }
106 // Do a second pass to check for explicitly disabled categories
107 // (those explicitly enabled have priority due to first pass).
108 category_group_tokens.Reset();
109 bool category_group_disabled = false;
110 while (category_group_tokens.GetNext()) {
111 StringPiece category_group_token = category_group_tokens.token_piece();
112 for (const std::string& category : excluded_categories_) {
113 if (MatchPattern(category_group_token, category)) {
114 // Current token of category_group_name is present in excluded_list.
115 // Flag the exclusion and proceed further to check if any of the
116 // remaining categories of category_group_name is not present in the
117 // excluded_ list.
118 category_group_disabled = true;
119 break;
120 }
121 // One of the category of category_group_name is not present in
122 // excluded_ list. So, if it's not a disabled-by-default category,
123 // it has to be included_ list. Enable the category_group_name
124 // for recording.
125 if (!MatchPattern(category_group_token, TRACE_DISABLED_BY_DEFAULT("*")))
126 category_group_disabled = false;
127 }
128 // One of the categories present in category_group_name is not present in
129 // excluded_ list. Implies this category_group_name group can be enabled
130 // for recording, since one of its groups is enabled for recording.
131 if (!category_group_disabled)
132 break;
133 }
134 // If the category group is not excluded, and there are no included patterns
135 // we consider this category group enabled, as long as it had categories
136 // other than disabled-by-default.
137 return !category_group_disabled && had_enabled_by_default &&
138 included_categories_.empty();
139 }
140
IsCategoryEnabled(const StringPiece & category_name) const141 bool TraceConfigCategoryFilter::IsCategoryEnabled(
142 const StringPiece& category_name) const {
143 // Check the disabled- filters and the disabled-* wildcard first so that a
144 // "*" filter does not include the disabled.
145 for (const std::string& category : disabled_categories_) {
146 if (MatchPattern(category_name, category))
147 return true;
148 }
149
150 if (MatchPattern(category_name, TRACE_DISABLED_BY_DEFAULT("*")))
151 return false;
152
153 for (const std::string& category : included_categories_) {
154 if (MatchPattern(category_name, category))
155 return true;
156 }
157
158 return false;
159 }
160
Merge(const TraceConfigCategoryFilter & config)161 void TraceConfigCategoryFilter::Merge(const TraceConfigCategoryFilter& config) {
162 // Keep included patterns only if both filters have an included entry.
163 // Otherwise, one of the filter was specifying "*" and we want to honor the
164 // broadest filter.
165 if (!included_categories_.empty() && !config.included_categories_.empty()) {
166 included_categories_.insert(included_categories_.end(),
167 config.included_categories_.begin(),
168 config.included_categories_.end());
169 } else {
170 included_categories_.clear();
171 }
172
173 disabled_categories_.insert(disabled_categories_.end(),
174 config.disabled_categories_.begin(),
175 config.disabled_categories_.end());
176 excluded_categories_.insert(excluded_categories_.end(),
177 config.excluded_categories_.begin(),
178 config.excluded_categories_.end());
179 synthetic_delays_.insert(synthetic_delays_.end(),
180 config.synthetic_delays_.begin(),
181 config.synthetic_delays_.end());
182 }
183
Clear()184 void TraceConfigCategoryFilter::Clear() {
185 included_categories_.clear();
186 disabled_categories_.clear();
187 excluded_categories_.clear();
188 synthetic_delays_.clear();
189 }
190
ToDict(DictionaryValue * dict) const191 void TraceConfigCategoryFilter::ToDict(DictionaryValue* dict) const {
192 StringList categories(included_categories_);
193 categories.insert(categories.end(), disabled_categories_.begin(),
194 disabled_categories_.end());
195 AddCategoriesToDict(categories, kIncludedCategoriesParam, dict);
196 AddCategoriesToDict(excluded_categories_, kExcludedCategoriesParam, dict);
197 AddCategoriesToDict(synthetic_delays_, kSyntheticDelaysParam, dict);
198 }
199
ToFilterString() const200 std::string TraceConfigCategoryFilter::ToFilterString() const {
201 std::string filter_string;
202 WriteCategoryFilterString(included_categories_, &filter_string, true);
203 WriteCategoryFilterString(disabled_categories_, &filter_string, true);
204 WriteCategoryFilterString(excluded_categories_, &filter_string, false);
205 WriteCategoryFilterString(synthetic_delays_, &filter_string);
206 return filter_string;
207 }
208
SetCategoriesFromIncludedList(const ListValue & included_list)209 void TraceConfigCategoryFilter::SetCategoriesFromIncludedList(
210 const ListValue& included_list) {
211 included_categories_.clear();
212 for (size_t i = 0; i < included_list.GetSize(); ++i) {
213 std::string category;
214 if (!included_list.GetString(i, &category))
215 continue;
216 if (category.compare(0, strlen(TRACE_DISABLED_BY_DEFAULT("")),
217 TRACE_DISABLED_BY_DEFAULT("")) == 0) {
218 disabled_categories_.push_back(category);
219 } else {
220 included_categories_.push_back(category);
221 }
222 }
223 }
224
SetCategoriesFromExcludedList(const ListValue & excluded_list)225 void TraceConfigCategoryFilter::SetCategoriesFromExcludedList(
226 const ListValue& excluded_list) {
227 excluded_categories_.clear();
228 for (size_t i = 0; i < excluded_list.GetSize(); ++i) {
229 std::string category;
230 if (excluded_list.GetString(i, &category))
231 excluded_categories_.push_back(category);
232 }
233 }
234
SetSyntheticDelaysFromList(const ListValue & list)235 void TraceConfigCategoryFilter::SetSyntheticDelaysFromList(
236 const ListValue& list) {
237 for (size_t i = 0; i < list.GetSize(); ++i) {
238 std::string delay;
239 if (!list.GetString(i, &delay))
240 continue;
241 // Synthetic delays are of the form "delay;option;option;...".
242 size_t name_length = delay.find(';');
243 if (name_length != std::string::npos && name_length > 0 &&
244 name_length != delay.size() - 1) {
245 synthetic_delays_.push_back(delay);
246 }
247 }
248 }
249
AddCategoriesToDict(const StringList & categories,const char * param,DictionaryValue * dict) const250 void TraceConfigCategoryFilter::AddCategoriesToDict(
251 const StringList& categories,
252 const char* param,
253 DictionaryValue* dict) const {
254 if (categories.empty())
255 return;
256
257 auto list = MakeUnique<ListValue>();
258 for (const std::string& category : categories)
259 list->AppendString(category);
260 dict->Set(param, std::move(list));
261 }
262
WriteCategoryFilterString(const StringList & values,std::string * out,bool included) const263 void TraceConfigCategoryFilter::WriteCategoryFilterString(
264 const StringList& values,
265 std::string* out,
266 bool included) const {
267 bool prepend_comma = !out->empty();
268 int token_cnt = 0;
269 for (const std::string& category : values) {
270 if (token_cnt > 0 || prepend_comma)
271 StringAppendF(out, ",");
272 StringAppendF(out, "%s%s", (included ? "" : "-"), category.c_str());
273 ++token_cnt;
274 }
275 }
276
WriteCategoryFilterString(const StringList & delays,std::string * out) const277 void TraceConfigCategoryFilter::WriteCategoryFilterString(
278 const StringList& delays,
279 std::string* out) const {
280 bool prepend_comma = !out->empty();
281 int token_cnt = 0;
282 for (const std::string& category : delays) {
283 if (token_cnt > 0 || prepend_comma)
284 StringAppendF(out, ",");
285 StringAppendF(out, "%s%s)", kSyntheticDelayCategoryFilterPrefix,
286 category.c_str());
287 ++token_cnt;
288 }
289 }
290
291 // static
IsCategoryNameAllowed(StringPiece str)292 bool TraceConfigCategoryFilter::IsCategoryNameAllowed(StringPiece str) {
293 return !str.empty() && str.front() != ' ' && str.back() != ' ';
294 }
295
296 } // namespace trace_event
297 } // namespace base
298