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 26 TraceConfigCategoryFilter::TraceConfigCategoryFilter() {} 27 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 35 TraceConfigCategoryFilter::~TraceConfigCategoryFilter() {} 36 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 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 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 89 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 141 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 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 184 void TraceConfigCategoryFilter::Clear() { 185 included_categories_.clear(); 186 disabled_categories_.clear(); 187 excluded_categories_.clear(); 188 synthetic_delays_.clear(); 189 } 190 191 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 200 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 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 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 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 250 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 263 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 277 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 292 bool TraceConfigCategoryFilter::IsCategoryNameAllowed(StringPiece str) { 293 return !str.empty() && str.front() != ' ' && str.back() != ' '; 294 } 295 296 } // namespace trace_event 297 } // namespace base 298