1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/trace_event/category_registry.h"
6 
7 #include <string.h>
8 
9 #include <type_traits>
10 
11 #include "base/atomicops.h"
12 #include "base/debug/leak_annotations.h"
13 #include "base/logging.h"
14 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
15 #include "base/trace_event/trace_category.h"
16 
17 namespace base {
18 namespace trace_event {
19 
20 namespace {
21 
22 constexpr size_t kMaxCategories = 200;
23 const int kNumBuiltinCategories = 4;
24 
25 // |g_categories| might end up causing creating dynamic initializers if not POD.
26 static_assert(std::is_pod<TraceCategory>::value, "TraceCategory must be POD");
27 
28 // These entries must be kept consistent with the kCategory* consts below.
29 TraceCategory g_categories[kMaxCategories] = {
30     {0, 0, "tracing categories exhausted; must increase kMaxCategories"},
31     {0, 0, "tracing already shutdown"},  // See kCategoryAlreadyShutdown below.
32     {0, 0, "__metadata"},                // See kCategoryMetadata below.
33     {0, 0, "toplevel"},                  // Warmup the toplevel category.
34 };
35 
36 base::subtle::AtomicWord g_category_index = kNumBuiltinCategories;
37 
IsValidCategoryPtr(const TraceCategory * category)38 bool IsValidCategoryPtr(const TraceCategory* category) {
39   // If any of these are hit, something has cached a corrupt category pointer.
40   uintptr_t ptr = reinterpret_cast<uintptr_t>(category);
41   return ptr % sizeof(void*) == 0 &&
42          ptr >= reinterpret_cast<uintptr_t>(&g_categories[0]) &&
43          ptr <= reinterpret_cast<uintptr_t>(&g_categories[kMaxCategories - 1]);
44 }
45 
46 }  // namespace
47 
48 // static
49 TraceCategory* const CategoryRegistry::kCategoryExhausted = &g_categories[0];
50 TraceCategory* const CategoryRegistry::kCategoryAlreadyShutdown =
51     &g_categories[1];
52 TraceCategory* const CategoryRegistry::kCategoryMetadata = &g_categories[2];
53 
54 // static
Initialize()55 void CategoryRegistry::Initialize() {
56   // Trace is enabled or disabled on one thread while other threads are
57   // accessing the enabled flag. We don't care whether edge-case events are
58   // traced or not, so we allow races on the enabled flag to keep the trace
59   // macros fast.
60   for (size_t i = 0; i < kMaxCategories; ++i) {
61     ANNOTATE_BENIGN_RACE(g_categories[i].state_ptr(),
62                          "trace_event category enabled");
63     // If this DCHECK is hit in a test it means that ResetForTesting() is not
64     // called and the categories state leaks between test fixtures.
65     DCHECK(!g_categories[i].is_enabled());
66   }
67 }
68 
69 // static
ResetForTesting()70 void CategoryRegistry::ResetForTesting() {
71   // reset_for_testing clears up only the enabled state and filters. The
72   // categories themselves cannot be cleared up because the static pointers
73   // injected by the macros still point to them and cannot be reset.
74   for (size_t i = 0; i < kMaxCategories; ++i)
75     g_categories[i].reset_for_testing();
76 }
77 
78 // static
GetCategoryByName(const char * category_name)79 TraceCategory* CategoryRegistry::GetCategoryByName(const char* category_name) {
80   DCHECK(!strchr(category_name, '"'))
81       << "Category names may not contain double quote";
82 
83   // The g_categories is append only, avoid using a lock for the fast path.
84   size_t category_index = base::subtle::Acquire_Load(&g_category_index);
85 
86   // Search for pre-existing category group.
87   for (size_t i = 0; i < category_index; ++i) {
88     if (strcmp(g_categories[i].name(), category_name) == 0) {
89       return &g_categories[i];
90     }
91   }
92   return nullptr;
93 }
94 
GetOrCreateCategoryLocked(const char * category_name,CategoryInitializerFn category_initializer_fn,TraceCategory ** category)95 bool CategoryRegistry::GetOrCreateCategoryLocked(
96     const char* category_name,
97     CategoryInitializerFn category_initializer_fn,
98     TraceCategory** category) {
99   // This is the slow path: the lock is not held in the fastpath
100   // (GetCategoryByName), so more than one thread could have reached here trying
101   // to add the same category.
102   *category = GetCategoryByName(category_name);
103   if (*category)
104     return false;
105 
106   // Create a new category.
107   size_t category_index = base::subtle::Acquire_Load(&g_category_index);
108   if (category_index >= kMaxCategories) {
109     NOTREACHED() << "must increase kMaxCategories";
110     *category = kCategoryExhausted;
111     return false;
112   }
113 
114   // TODO(primiano): this strdup should be removed. The only documented reason
115   // for it was TraceWatchEvent, which is gone. However, something might have
116   // ended up relying on this. Needs some auditing before removal.
117   const char* category_name_copy = strdup(category_name);
118   ANNOTATE_LEAKING_OBJECT_PTR(category_name_copy);
119 
120   *category = &g_categories[category_index];
121   DCHECK(!(*category)->is_valid());
122   DCHECK(!(*category)->is_enabled());
123   (*category)->set_name(category_name_copy);
124   category_initializer_fn(*category);
125 
126   // Update the max index now.
127   base::subtle::Release_Store(&g_category_index, category_index + 1);
128   return true;
129 }
130 
131 // static
GetCategoryByStatePtr(const uint8_t * category_state)132 const TraceCategory* CategoryRegistry::GetCategoryByStatePtr(
133     const uint8_t* category_state) {
134   const TraceCategory* category = TraceCategory::FromStatePtr(category_state);
135   DCHECK(IsValidCategoryPtr(category));
136   return category;
137 }
138 
139 // static
IsBuiltinCategory(const TraceCategory * category)140 bool CategoryRegistry::IsBuiltinCategory(const TraceCategory* category) {
141   DCHECK(IsValidCategoryPtr(category));
142   return category < &g_categories[kNumBuiltinCategories];
143 }
144 
145 // static
GetAllCategories()146 CategoryRegistry::Range CategoryRegistry::GetAllCategories() {
147   // The |g_categories| array is append only. We have to only guarantee to
148   // not return an index to a category which is being initialized by
149   // GetOrCreateCategoryByName().
150   size_t category_index = base::subtle::Acquire_Load(&g_category_index);
151   return CategoryRegistry::Range(&g_categories[0],
152                                  &g_categories[category_index]);
153 }
154 
155 }  // namespace trace_event
156 }  // namespace base
157