| Index: base/trace_event/category_registry.cc
|
| diff --git a/base/trace_event/category_registry.cc b/base/trace_event/category_registry.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..87715fc806a4efb9d37f58f71e2f560ca85b1810
|
| --- /dev/null
|
| +++ b/base/trace_event/category_registry.cc
|
| @@ -0,0 +1,162 @@
|
| +// Copyright 2016 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/category_registry.h"
|
| +
|
| +#include <string.h>
|
| +
|
| +#include <type_traits>
|
| +
|
| +#include "base/atomicops.h"
|
| +#include "base/debug/leak_annotations.h"
|
| +#include "base/lazy_instance.h"
|
| +#include "base/logging.h"
|
| +#include "base/synchronization/lock.h"
|
| +#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
|
| +#include "base/trace_event/trace_category.h"
|
| +
|
| +namespace base {
|
| +namespace trace_event {
|
| +
|
| +namespace {
|
| +
|
| +constexpr size_t kMaxCategories = 200;
|
| +const int kNumBuiltinCategories = 4;
|
| +
|
| +// |g_categories| might end up causing creating dynamic initializers if not POD.
|
| +static_assert(std::is_pod<TraceCategory>::value, "TraceCategory must be POD");
|
| +
|
| +// These entries must be kept consistent with the kCategory* consts below.
|
| +TraceCategory g_categories[kMaxCategories] = {
|
| + {0, 0, "tracing categories exhausted; must increase kMaxCategories"},
|
| + {0, 0, "tracing already shutdown"}, // See kCategoryAlreadyShutdown below.
|
| + {0, 0, "__metadata"}, // See kCategoryMetadata below.
|
| + {0, 0, "toplevel"}, // Warmup the toplevel category.
|
| +};
|
| +
|
| +base::subtle::AtomicWord g_category_index = kNumBuiltinCategories;
|
| +
|
| +base::LazyInstance<base::Lock>::Leaky g_category_lock =
|
| + LAZY_INSTANCE_INITIALIZER;
|
| +
|
| +bool IsValidCategoryPtr(const TraceCategory* category) {
|
| + // If any of these are hit, something has cached a corrupt category pointer.
|
| + uintptr_t ptr = reinterpret_cast<uintptr_t>(category);
|
| + return ptr % sizeof(void*) == 0 &&
|
| + ptr >= reinterpret_cast<uintptr_t>(&g_categories[0]) &&
|
| + ptr <= reinterpret_cast<uintptr_t>(&g_categories[kMaxCategories - 1]);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// static
|
| +TraceCategory* const CategoryRegistry::kCategoryExhausted = &g_categories[0];
|
| +TraceCategory* const CategoryRegistry::kCategoryAlreadyShutdown =
|
| + &g_categories[1];
|
| +TraceCategory* const CategoryRegistry::kCategoryMetadata = &g_categories[2];
|
| +
|
| +// static
|
| +void CategoryRegistry::Initialize() {
|
| + // Trace is enabled or disabled on one thread while other threads are
|
| + // accessing the enabled flag. We don't care whether edge-case events are
|
| + // traced or not, so we allow races on the enabled flag to keep the trace
|
| + // macros fast.
|
| + for (size_t i = 0; i < kMaxCategories; ++i) {
|
| + ANNOTATE_BENIGN_RACE(g_categories[i].state_ptr(),
|
| + "trace_event category enabled");
|
| + // If this DCHECK is hit in a test it means that ResetForTesting() is not
|
| + // called and the categories state leaks between test fixtures.
|
| + DCHECK(!g_categories[i].is_enabled());
|
| + }
|
| +}
|
| +
|
| +// static
|
| +void CategoryRegistry::ResetForTesting() {
|
| + AutoLock lock(g_category_lock.Get());
|
| + for (size_t i = 0; i < kMaxCategories; ++i)
|
| + g_categories[i].reset_for_testing();
|
| +}
|
| +
|
| +// static
|
| +bool CategoryRegistry::GetOrCreateCategoryByName(const char* category_name,
|
| + TraceCategory** category) {
|
| + DCHECK(!strchr(category_name, '"'))
|
| + << "Category names may not contain double quote";
|
| +
|
| + // The g_categories is append only, avoid using a lock for the fast path.
|
| + size_t category_index = base::subtle::Acquire_Load(&g_category_index);
|
| +
|
| + // Search for pre-existing category group.
|
| + for (size_t i = 0; i < category_index; ++i) {
|
| + if (strcmp(g_categories[i].name(), category_name) == 0) {
|
| + *category = &g_categories[i];
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + // This is the slow path: the lock is not held in the case above, so more
|
| + // than one thread could have reached here trying to add the same category.
|
| + // Only hold the lock when actually appending a new category, and check the
|
| + // categories groups again.
|
| + // TODO(primiano): there should be no need for the acquire/release semantics
|
| + // on g_category_index below, the outer lock implies that. Remove once the
|
| + // tracing refactoring reaches a quieter state and we can afford the risk.
|
| + AutoLock lock(g_category_lock.Get());
|
| + category_index = base::subtle::Acquire_Load(&g_category_index);
|
| + for (size_t i = 0; i < category_index; ++i) {
|
| + if (strcmp(g_categories[i].name(), category_name) == 0) {
|
| + *category = &g_categories[i];
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + // Create a new category.
|
| + if (category_index >= kMaxCategories) {
|
| + NOTREACHED() << "must increase kMaxCategories";
|
| + *category = kCategoryExhausted;
|
| + return false;
|
| + }
|
| +
|
| + // TODO(primiano): this strdup should be removed. The only documented reason
|
| + // for it was TraceWatchEvent, which is gone. However, something might have
|
| + // ended up relying on this. Needs some auditing before removal.
|
| + const char* category_name_copy = strdup(category_name);
|
| + ANNOTATE_LEAKING_OBJECT_PTR(category_name_copy);
|
| +
|
| + *category = &g_categories[category_index];
|
| + DCHECK(!(*category)->is_valid());
|
| + DCHECK(!(*category)->is_enabled());
|
| + (*category)->set_name(category_name_copy);
|
| +
|
| + // Update the max index now.
|
| + base::subtle::Release_Store(&g_category_index, category_index + 1);
|
| + return true;
|
| +}
|
| +
|
| +// static
|
| +const TraceCategory* CategoryRegistry::GetCategoryByStatePtr(
|
| + const uint8_t* category_state) {
|
| + const TraceCategory* category = TraceCategory::FromStatePtr(category_state);
|
| + DCHECK(IsValidCategoryPtr(category));
|
| + return category;
|
| +}
|
| +
|
| +// static
|
| +bool CategoryRegistry::IsBuiltinCategory(const TraceCategory* category) {
|
| + DCHECK(IsValidCategoryPtr(category));
|
| + return category < &g_categories[kNumBuiltinCategories];
|
| +}
|
| +
|
| +// static
|
| +CategoryRegistry::Range CategoryRegistry::GetAllCategories() {
|
| + // The |g_categories| array is append only. We have to only guarantee to
|
| + // not return an index to a category which is being initialized by
|
| + // GetOrCreateCategoryByName().
|
| + size_t category_index = base::subtle::Acquire_Load(&g_category_index);
|
| + return CategoryRegistry::Range(&g_categories[0],
|
| + &g_categories[category_index]);
|
| +}
|
| +
|
| +} // namespace trace_event
|
| +} // namespace base
|
|
|