Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(100)

Side by Side Diff: base/trace_event/category_registry.cc

Issue 2495173002: tracing: fix races in CategoryRegistry (Closed)
Patch Set: . Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/trace_event/category_registry.h" 5 #include "base/trace_event/category_registry.h"
6 6
7 #include <string.h> 7 #include <string.h>
8 8
9 #include <type_traits> 9 #include <type_traits>
10 10
11 #include "base/atomicops.h" 11 #include "base/atomicops.h"
12 #include "base/debug/leak_annotations.h" 12 #include "base/debug/leak_annotations.h"
13 #include "base/lazy_instance.h"
14 #include "base/logging.h" 13 #include "base/logging.h"
15 #include "base/synchronization/lock.h"
16 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" 14 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
17 #include "base/trace_event/trace_category.h" 15 #include "base/trace_event/trace_category.h"
18 16
19 namespace base { 17 namespace base {
20 namespace trace_event { 18 namespace trace_event {
21 19
22 namespace { 20 namespace {
23 21
24 constexpr size_t kMaxCategories = 200; 22 constexpr size_t kMaxCategories = 200;
25 const int kNumBuiltinCategories = 4; 23 const int kNumBuiltinCategories = 4;
26 24
27 // |g_categories| might end up causing creating dynamic initializers if not POD. 25 // |g_categories| might end up causing creating dynamic initializers if not POD.
28 static_assert(std::is_pod<TraceCategory>::value, "TraceCategory must be POD"); 26 static_assert(std::is_pod<TraceCategory>::value, "TraceCategory must be POD");
29 27
30 // These entries must be kept consistent with the kCategory* consts below. 28 // These entries must be kept consistent with the kCategory* consts below.
31 TraceCategory g_categories[kMaxCategories] = { 29 TraceCategory g_categories[kMaxCategories] = {
32 {0, 0, "tracing categories exhausted; must increase kMaxCategories"}, 30 {0, 0, "tracing categories exhausted; must increase kMaxCategories"},
33 {0, 0, "tracing already shutdown"}, // See kCategoryAlreadyShutdown below. 31 {0, 0, "tracing already shutdown"}, // See kCategoryAlreadyShutdown below.
34 {0, 0, "__metadata"}, // See kCategoryMetadata below. 32 {0, 0, "__metadata"}, // See kCategoryMetadata below.
35 {0, 0, "toplevel"}, // Warmup the toplevel category. 33 {0, 0, "toplevel"}, // Warmup the toplevel category.
36 }; 34 };
37 35
38 base::subtle::AtomicWord g_category_index = kNumBuiltinCategories; 36 base::subtle::AtomicWord g_category_index = kNumBuiltinCategories;
39 37
40 base::LazyInstance<base::Lock>::Leaky g_category_lock =
41 LAZY_INSTANCE_INITIALIZER;
42
43 bool IsValidCategoryPtr(const TraceCategory* category) { 38 bool IsValidCategoryPtr(const TraceCategory* category) {
44 // If any of these are hit, something has cached a corrupt category pointer. 39 // If any of these are hit, something has cached a corrupt category pointer.
45 uintptr_t ptr = reinterpret_cast<uintptr_t>(category); 40 uintptr_t ptr = reinterpret_cast<uintptr_t>(category);
46 return ptr % sizeof(void*) == 0 && 41 return ptr % sizeof(void*) == 0 &&
47 ptr >= reinterpret_cast<uintptr_t>(&g_categories[0]) && 42 ptr >= reinterpret_cast<uintptr_t>(&g_categories[0]) &&
48 ptr <= reinterpret_cast<uintptr_t>(&g_categories[kMaxCategories - 1]); 43 ptr <= reinterpret_cast<uintptr_t>(&g_categories[kMaxCategories - 1]);
49 } 44 }
50 45
51 } // namespace 46 } // namespace
52 47
(...skipping 13 matching lines...) Expand all
66 ANNOTATE_BENIGN_RACE(g_categories[i].state_ptr(), 61 ANNOTATE_BENIGN_RACE(g_categories[i].state_ptr(),
67 "trace_event category enabled"); 62 "trace_event category enabled");
68 // If this DCHECK is hit in a test it means that ResetForTesting() is not 63 // If this DCHECK is hit in a test it means that ResetForTesting() is not
69 // called and the categories state leaks between test fixtures. 64 // called and the categories state leaks between test fixtures.
70 DCHECK(!g_categories[i].is_enabled()); 65 DCHECK(!g_categories[i].is_enabled());
71 } 66 }
72 } 67 }
73 68
74 // static 69 // static
75 void CategoryRegistry::ResetForTesting() { 70 void CategoryRegistry::ResetForTesting() {
76 AutoLock lock(g_category_lock.Get()); 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.
77 for (size_t i = 0; i < kMaxCategories; ++i) 74 for (size_t i = 0; i < kMaxCategories; ++i)
78 g_categories[i].reset_for_testing(); 75 g_categories[i].reset_for_testing();
79 } 76 }
80 77
81 // static 78 // static
82 bool CategoryRegistry::GetOrCreateCategoryByName(const char* category_name, 79 TraceCategory* CategoryRegistry::GetCategoryByName(const char* category_name) {
83 TraceCategory** category) {
84 DCHECK(!strchr(category_name, '"')) 80 DCHECK(!strchr(category_name, '"'))
85 << "Category names may not contain double quote"; 81 << "Category names may not contain double quote";
86 82
87 // The g_categories is append only, avoid using a lock for the fast path. 83 // The g_categories is append only, avoid using a lock for the fast path.
88 size_t category_index = base::subtle::Acquire_Load(&g_category_index); 84 size_t category_index = base::subtle::Acquire_Load(&g_category_index);
89 85
90 // Search for pre-existing category group. 86 // Search for pre-existing category group.
91 for (size_t i = 0; i < category_index; ++i) { 87 for (size_t i = 0; i < category_index; ++i) {
92 if (strcmp(g_categories[i].name(), category_name) == 0) { 88 if (strcmp(g_categories[i].name(), category_name) == 0) {
93 *category = &g_categories[i]; 89 return &g_categories[i];
94 return false;
95 } 90 }
96 } 91 }
92 return nullptr;
93 }
97 94
98 // This is the slow path: the lock is not held in the case above, so more 95 bool CategoryRegistry::GetOrCreateCategoryLocked(
99 // than one thread could have reached here trying to add the same category. 96 const char* category_name,
100 // Only hold the lock when actually appending a new category, and check the 97 CategoryInitializerFn category_initializer_fn,
101 // categories groups again. 98 TraceCategory** category) {
102 // TODO(primiano): there should be no need for the acquire/release semantics 99 // This is the slow path: the lock is not held in the fastpath
103 // on g_category_index below, the outer lock implies that. Remove once the 100 // (GetCategoryByName), so more than one thread could have reached here trying
104 // tracing refactoring reaches a quieter state and we can afford the risk. 101 // to add the same category.
105 AutoLock lock(g_category_lock.Get()); 102 *category = GetCategoryByName(category_name);
106 category_index = base::subtle::Acquire_Load(&g_category_index); 103 if (*category)
107 for (size_t i = 0; i < category_index; ++i) { 104 return false;
108 if (strcmp(g_categories[i].name(), category_name) == 0) {
109 *category = &g_categories[i];
110 return false;
111 }
112 }
113 105
114 // Create a new category. 106 // Create a new category.
107 size_t category_index = base::subtle::Acquire_Load(&g_category_index);
115 if (category_index >= kMaxCategories) { 108 if (category_index >= kMaxCategories) {
116 NOTREACHED() << "must increase kMaxCategories"; 109 NOTREACHED() << "must increase kMaxCategories";
117 *category = kCategoryExhausted; 110 *category = kCategoryExhausted;
118 return false; 111 return false;
119 } 112 }
120 113
121 // TODO(primiano): this strdup should be removed. The only documented reason 114 // TODO(primiano): this strdup should be removed. The only documented reason
122 // for it was TraceWatchEvent, which is gone. However, something might have 115 // for it was TraceWatchEvent, which is gone. However, something might have
123 // ended up relying on this. Needs some auditing before removal. 116 // ended up relying on this. Needs some auditing before removal.
124 const char* category_name_copy = strdup(category_name); 117 const char* category_name_copy = strdup(category_name);
125 ANNOTATE_LEAKING_OBJECT_PTR(category_name_copy); 118 ANNOTATE_LEAKING_OBJECT_PTR(category_name_copy);
126 119
127 *category = &g_categories[category_index]; 120 *category = &g_categories[category_index];
128 DCHECK(!(*category)->is_valid()); 121 DCHECK(!(*category)->is_valid());
129 DCHECK(!(*category)->is_enabled()); 122 DCHECK(!(*category)->is_enabled());
130 (*category)->set_name(category_name_copy); 123 (*category)->set_name(category_name_copy);
124 category_initializer_fn(*category);
131 125
132 // Update the max index now. 126 // Update the max index now.
133 base::subtle::Release_Store(&g_category_index, category_index + 1); 127 base::subtle::Release_Store(&g_category_index, category_index + 1);
134 return true; 128 return true;
135 } 129 }
136 130
137 // static 131 // static
138 const TraceCategory* CategoryRegistry::GetCategoryByStatePtr( 132 const TraceCategory* CategoryRegistry::GetCategoryByStatePtr(
139 const uint8_t* category_state) { 133 const uint8_t* category_state) {
140 const TraceCategory* category = TraceCategory::FromStatePtr(category_state); 134 const TraceCategory* category = TraceCategory::FromStatePtr(category_state);
(...skipping 12 matching lines...) Expand all
153 // The |g_categories| array is append only. We have to only guarantee to 147 // The |g_categories| array is append only. We have to only guarantee to
154 // not return an index to a category which is being initialized by 148 // not return an index to a category which is being initialized by
155 // GetOrCreateCategoryByName(). 149 // GetOrCreateCategoryByName().
156 size_t category_index = base::subtle::Acquire_Load(&g_category_index); 150 size_t category_index = base::subtle::Acquire_Load(&g_category_index);
157 return CategoryRegistry::Range(&g_categories[0], 151 return CategoryRegistry::Range(&g_categories[0],
158 &g_categories[category_index]); 152 &g_categories[category_index]);
159 } 153 }
160 154
161 } // namespace trace_event 155 } // namespace trace_event
162 } // namespace base 156 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698