diff options
Diffstat (limited to 'base/trace_event/category_registry.cc')
-rw-r--r-- | base/trace_event/category_registry.cc | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/base/trace_event/category_registry.cc b/base/trace_event/category_registry.cc new file mode 100644 index 0000000000..e7c14606d6 --- /dev/null +++ b/base/trace_event/category_registry.cc @@ -0,0 +1,156 @@ +// 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/logging.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; + +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() { + // reset_for_testing clears up only the enabled state and filters. The + // categories themselves cannot be cleared up because the static pointers + // injected by the macros still point to them and cannot be reset. + for (size_t i = 0; i < kMaxCategories; ++i) + g_categories[i].reset_for_testing(); +} + +// static +TraceCategory* CategoryRegistry::GetCategoryByName(const char* category_name) { + 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) { + return &g_categories[i]; + } + } + return nullptr; +} + +bool CategoryRegistry::GetOrCreateCategoryLocked( + const char* category_name, + CategoryInitializerFn category_initializer_fn, + TraceCategory** category) { + // This is the slow path: the lock is not held in the fastpath + // (GetCategoryByName), so more than one thread could have reached here trying + // to add the same category. + *category = GetCategoryByName(category_name); + if (*category) + return false; + + // Create a new category. + size_t category_index = base::subtle::Acquire_Load(&g_category_index); + 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); + category_initializer_fn(*category); + + // 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 |