summaryrefslogtreecommitdiff
path: root/base/trace_event/category_registry.cc
diff options
context:
space:
mode:
Diffstat (limited to 'base/trace_event/category_registry.cc')
-rw-r--r--base/trace_event/category_registry.cc156
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