| // 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 |