blob: 16c3da54bfbd5d13ff4a4cdd8ce7b0e82e7b3122 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/trace_event/trace_config_category_filter.h"
#include "base/memory/ptr_util.h"
#include "base/strings/pattern.h"
#include "base/strings/string_split.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/trace_event.h"
#include "starboard/common/string.h"
namespace base {
namespace trace_event {
namespace {
const char kIncludedCategoriesParam[] = "included_categories";
const char kExcludedCategoriesParam[] = "excluded_categories";
}
TraceConfigCategoryFilter::TraceConfigCategoryFilter() = default;
TraceConfigCategoryFilter::TraceConfigCategoryFilter(
const TraceConfigCategoryFilter& other) = default;
TraceConfigCategoryFilter::~TraceConfigCategoryFilter() = default;
TraceConfigCategoryFilter& TraceConfigCategoryFilter::operator=(
const TraceConfigCategoryFilter& rhs) = default;
void TraceConfigCategoryFilter::InitializeFromString(
const StringPiece& category_filter_string) {
std::vector<StringPiece> split = SplitStringPiece(
category_filter_string, ",", TRIM_WHITESPACE, SPLIT_WANT_ALL);
for (const StringPiece& category : split) {
// Ignore empty categories.
if (category.empty())
continue;
if (category.front() == '-') {
// Excluded categories start with '-'.
// Remove '-' from category string.
excluded_categories_.push_back(category.substr(1).as_string());
} else if (category.starts_with(TRACE_DISABLED_BY_DEFAULT(""))) {
disabled_categories_.push_back(category.as_string());
} else {
included_categories_.push_back(category.as_string());
}
}
}
void TraceConfigCategoryFilter::InitializeFromConfigDict(
const DictionaryValue& dict) {
const ListValue* category_list = nullptr;
if (dict.GetList(kIncludedCategoriesParam, &category_list))
SetCategoriesFromIncludedList(*category_list);
if (dict.GetList(kExcludedCategoriesParam, &category_list))
SetCategoriesFromExcludedList(*category_list);
}
bool TraceConfigCategoryFilter::IsCategoryGroupEnabled(
const StringPiece& category_group_name) const {
bool had_enabled_by_default = false;
DCHECK(!category_group_name.empty());
CStringTokenizer category_group_tokens(category_group_name.begin(),
category_group_name.end(), ",");
while (category_group_tokens.GetNext()) {
StringPiece category_group_token = category_group_tokens.token_piece();
// Don't allow empty tokens, nor tokens with leading or trailing space.
DCHECK(IsCategoryNameAllowed(category_group_token))
<< "Disallowed category string";
if (IsCategoryEnabled(category_group_token))
return true;
if (!MatchPattern(category_group_token, TRACE_DISABLED_BY_DEFAULT("*")))
had_enabled_by_default = true;
}
// Do a second pass to check for explicitly disabled categories
// (those explicitly enabled have priority due to first pass).
category_group_tokens.Reset();
bool category_group_disabled = false;
while (category_group_tokens.GetNext()) {
StringPiece category_group_token = category_group_tokens.token_piece();
for (const std::string& category : excluded_categories_) {
if (MatchPattern(category_group_token, category)) {
// Current token of category_group_name is present in excluded_list.
// Flag the exclusion and proceed further to check if any of the
// remaining categories of category_group_name is not present in the
// excluded_ list.
category_group_disabled = true;
break;
}
// One of the category of category_group_name is not present in
// excluded_ list. So, if it's not a disabled-by-default category,
// it has to be included_ list. Enable the category_group_name
// for recording.
if (!MatchPattern(category_group_token, TRACE_DISABLED_BY_DEFAULT("*")))
category_group_disabled = false;
}
// One of the categories present in category_group_name is not present in
// excluded_ list. Implies this category_group_name group can be enabled
// for recording, since one of its groups is enabled for recording.
if (!category_group_disabled)
break;
}
// If the category group is not excluded, and there are no included patterns
// we consider this category group enabled, as long as it had categories
// other than disabled-by-default.
return !category_group_disabled && had_enabled_by_default &&
included_categories_.empty();
}
bool TraceConfigCategoryFilter::IsCategoryEnabled(
const StringPiece& category_name) const {
// Check the disabled- filters and the disabled-* wildcard first so that a
// "*" filter does not include the disabled.
for (const std::string& category : disabled_categories_) {
if (MatchPattern(category_name, category))
return true;
}
if (MatchPattern(category_name, TRACE_DISABLED_BY_DEFAULT("*")))
return false;
for (const std::string& category : included_categories_) {
if (MatchPattern(category_name, category))
return true;
}
return false;
}
void TraceConfigCategoryFilter::Merge(const TraceConfigCategoryFilter& config) {
// Keep included patterns only if both filters have an included entry.
// Otherwise, one of the filter was specifying "*" and we want to honor the
// broadest filter.
if (!included_categories_.empty() && !config.included_categories_.empty()) {
included_categories_.insert(included_categories_.end(),
config.included_categories_.begin(),
config.included_categories_.end());
} else {
included_categories_.clear();
}
disabled_categories_.insert(disabled_categories_.end(),
config.disabled_categories_.begin(),
config.disabled_categories_.end());
excluded_categories_.insert(excluded_categories_.end(),
config.excluded_categories_.begin(),
config.excluded_categories_.end());
}
void TraceConfigCategoryFilter::Clear() {
included_categories_.clear();
disabled_categories_.clear();
excluded_categories_.clear();
}
void TraceConfigCategoryFilter::ToDict(DictionaryValue* dict) const {
StringList categories(included_categories_);
categories.insert(categories.end(), disabled_categories_.begin(),
disabled_categories_.end());
AddCategoriesToDict(categories, kIncludedCategoriesParam, dict);
AddCategoriesToDict(excluded_categories_, kExcludedCategoriesParam, dict);
}
std::string TraceConfigCategoryFilter::ToFilterString() const {
std::string filter_string;
WriteCategoryFilterString(included_categories_, &filter_string, true);
WriteCategoryFilterString(disabled_categories_, &filter_string, true);
WriteCategoryFilterString(excluded_categories_, &filter_string, false);
return filter_string;
}
void TraceConfigCategoryFilter::SetCategoriesFromIncludedList(
const ListValue& included_list) {
included_categories_.clear();
for (size_t i = 0; i < included_list.GetSize(); ++i) {
std::string category;
if (!included_list.GetString(i, &category))
continue;
if (category.compare(0, SbStringGetLength(TRACE_DISABLED_BY_DEFAULT("")),
TRACE_DISABLED_BY_DEFAULT("")) == 0) {
disabled_categories_.push_back(category);
} else {
included_categories_.push_back(category);
}
}
}
void TraceConfigCategoryFilter::SetCategoriesFromExcludedList(
const ListValue& excluded_list) {
excluded_categories_.clear();
for (size_t i = 0; i < excluded_list.GetSize(); ++i) {
std::string category;
if (excluded_list.GetString(i, &category))
excluded_categories_.push_back(category);
}
}
void TraceConfigCategoryFilter::AddCategoriesToDict(
const StringList& categories,
const char* param,
DictionaryValue* dict) const {
if (categories.empty())
return;
auto list = std::make_unique<ListValue>();
for (const std::string& category : categories)
list->AppendString(category);
dict->Set(param, std::move(list));
}
void TraceConfigCategoryFilter::WriteCategoryFilterString(
const StringList& values,
std::string* out,
bool included) const {
bool prepend_comma = !out->empty();
int token_cnt = 0;
for (const std::string& category : values) {
if (token_cnt > 0 || prepend_comma)
StringAppendF(out, ",");
StringAppendF(out, "%s%s", (included ? "" : "-"), category.c_str());
++token_cnt;
}
}
// static
bool TraceConfigCategoryFilter::IsCategoryNameAllowed(StringPiece str) {
return !str.empty() && str.front() != ' ' && str.back() != ' ';
}
} // namespace trace_event
} // namespace base