• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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