Home | History | Annotate | Download | only in trace_event
      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