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 <string.h>
6
7 #include <memory>
8
9 #include "base/bind.h"
10 #include "base/lazy_instance.h"
11 #include "base/synchronization/lock.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "base/threading/thread.h"
14 #include "base/trace_event/category_registry.h"
15 #include "base/trace_event/trace_category.h"
16 #include "build/build_config.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18
19 namespace base {
20 namespace trace_event {
21
22 // Static initializers are generally forbidden. However, in the past we ran in
23 // the case of some test using tracing in a static initializer. This test checks
24 // That the category registry doesn't rely on static initializers itself and is
25 // functional even if called from another static initializer.
Initializer()26 bool Initializer() {
27 return CategoryRegistry::kCategoryMetadata &&
28 CategoryRegistry::kCategoryMetadata->is_valid();
29 }
30 bool g_initializer_check = Initializer();
31
32 class TraceCategoryTest : public testing::Test {
33 public:
SetUp()34 void SetUp() override { CategoryRegistry::Initialize(); }
35
TearDown()36 void TearDown() override { CategoryRegistry::ResetForTesting(); }
37
GetOrCreateCategoryByName(const char * name,TraceCategory ** cat)38 static bool GetOrCreateCategoryByName(const char* name, TraceCategory** cat) {
39 static LazyInstance<Lock>::Leaky g_lock = LAZY_INSTANCE_INITIALIZER;
40 bool is_new_cat = false;
41 *cat = CategoryRegistry::GetCategoryByName(name);
42 if (!*cat) {
43 AutoLock lock(g_lock.Get());
44 is_new_cat = CategoryRegistry::GetOrCreateCategoryLocked(
45 name, [](TraceCategory*) {}, cat);
46 }
47 return is_new_cat;
48 };
49
GetAllCategories()50 static CategoryRegistry::Range GetAllCategories() {
51 return CategoryRegistry::GetAllCategories();
52 }
53
TestRaceThreadMain(WaitableEvent * event)54 static void TestRaceThreadMain(WaitableEvent* event) {
55 TraceCategory* cat = nullptr;
56 event->Wait();
57 GetOrCreateCategoryByName("__test_race", &cat);
58 EXPECT_NE(nullptr, cat);
59 }
60 };
61
TEST_F(TraceCategoryTest,Basic)62 TEST_F(TraceCategoryTest, Basic) {
63 ASSERT_NE(nullptr, CategoryRegistry::kCategoryMetadata);
64 ASSERT_TRUE(CategoryRegistry::kCategoryMetadata->is_valid());
65 ASSERT_FALSE(CategoryRegistry::kCategoryMetadata->is_enabled());
66
67 // Metadata category is built-in and should create a new category.
68 TraceCategory* cat_meta = nullptr;
69 const char* kMetadataName = CategoryRegistry::kCategoryMetadata->name();
70 ASSERT_FALSE(GetOrCreateCategoryByName(kMetadataName, &cat_meta));
71 ASSERT_EQ(CategoryRegistry::kCategoryMetadata, cat_meta);
72
73 TraceCategory* cat_1 = nullptr;
74 ASSERT_TRUE(GetOrCreateCategoryByName("__test_basic_ab", &cat_1));
75 ASSERT_FALSE(cat_1->is_enabled());
76 ASSERT_EQ(0u, cat_1->enabled_filters());
77 cat_1->set_state_flag(TraceCategory::ENABLED_FOR_RECORDING);
78 cat_1->set_state_flag(TraceCategory::ENABLED_FOR_FILTERING);
79 ASSERT_EQ(TraceCategory::ENABLED_FOR_RECORDING |
80 TraceCategory::ENABLED_FOR_FILTERING,
81 cat_1->state());
82
83 cat_1->set_enabled_filters(129);
84 ASSERT_EQ(129u, cat_1->enabled_filters());
85 ASSERT_EQ(cat_1, CategoryRegistry::GetCategoryByStatePtr(cat_1->state_ptr()));
86
87 cat_1->clear_state_flag(TraceCategory::ENABLED_FOR_FILTERING);
88 ASSERT_EQ(TraceCategory::ENABLED_FOR_RECORDING, cat_1->state());
89 ASSERT_EQ(TraceCategory::ENABLED_FOR_RECORDING, *cat_1->state_ptr());
90 ASSERT_TRUE(cat_1->is_enabled());
91
92 TraceCategory* cat_2 = nullptr;
93 ASSERT_TRUE(GetOrCreateCategoryByName("__test_basic_a", &cat_2));
94 ASSERT_FALSE(cat_2->is_enabled());
95 cat_2->set_state_flag(TraceCategory::ENABLED_FOR_RECORDING);
96
97 TraceCategory* cat_2_copy = nullptr;
98 ASSERT_FALSE(GetOrCreateCategoryByName("__test_basic_a", &cat_2_copy));
99 ASSERT_EQ(cat_2, cat_2_copy);
100
101 TraceCategory* cat_3 = nullptr;
102 ASSERT_TRUE(
103 GetOrCreateCategoryByName("__test_basic_ab,__test_basic_a", &cat_3));
104 ASSERT_FALSE(cat_3->is_enabled());
105 ASSERT_EQ(0u, cat_3->enabled_filters());
106
107 int num_test_categories_seen = 0;
108 for (const TraceCategory& cat : GetAllCategories()) {
109 if (strcmp(cat.name(), kMetadataName) == 0)
110 ASSERT_TRUE(CategoryRegistry::IsBuiltinCategory(&cat));
111
112 if (strncmp(cat.name(), "__test_basic_", 13) == 0) {
113 ASSERT_FALSE(CategoryRegistry::IsBuiltinCategory(&cat));
114 num_test_categories_seen++;
115 }
116 }
117 ASSERT_EQ(3, num_test_categories_seen);
118 ASSERT_TRUE(g_initializer_check);
119 }
120
121 // Tries to cover the case of multiple threads creating the same category
122 // simultaneously. Should never end up with distinct entries with the same name.
123 #if defined(OS_FUCHSIA)
124 // TODO(crbug.com/738275): This is flaky on Fuchsia.
125 #define MAYBE_ThreadRaces DISABLED_ThreadRaces
126 #else
127 #define MAYBE_ThreadRaces ThreadRaces
128 #endif
TEST_F(TraceCategoryTest,MAYBE_ThreadRaces)129 TEST_F(TraceCategoryTest, MAYBE_ThreadRaces) {
130 const int kNumThreads = 32;
131 std::unique_ptr<Thread> threads[kNumThreads];
132 for (int i = 0; i < kNumThreads; i++) {
133 threads[i].reset(new Thread("test thread"));
134 threads[i]->Start();
135 }
136 WaitableEvent sync_event(WaitableEvent::ResetPolicy::MANUAL,
137 WaitableEvent::InitialState::NOT_SIGNALED);
138 for (int i = 0; i < kNumThreads; i++) {
139 threads[i]->task_runner()->PostTask(
140 FROM_HERE, BindOnce(&TestRaceThreadMain, Unretained(&sync_event)));
141 }
142 sync_event.Signal();
143 for (int i = 0; i < kNumThreads; i++)
144 threads[i]->Stop();
145
146 int num_times_seen = 0;
147 for (const TraceCategory& cat : GetAllCategories()) {
148 if (strcmp(cat.name(), "__test_race") == 0)
149 num_times_seen++;
150 }
151 ASSERT_EQ(1, num_times_seen);
152 }
153
154 } // namespace trace_event
155 } // namespace base
156