• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The Chromium Authors
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/containers/span.h"
10 #include "base/functional/bind.h"
11 #include "base/lazy_instance.h"
12 #include "base/synchronization/lock.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/threading/thread.h"
15 #include "base/trace_event/category_registry.h"
16 #include "base/trace_event/trace_category.h"
17 #include "build/build_config.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 
20 namespace base {
21 namespace trace_event {
22 
23 // Static initializers are generally forbidden. However, in the past we ran in
24 // the case of some test using tracing in a static initializer. This test checks
25 // That the category registry doesn't rely on static initializers itself and is
26 // functional even if called from another static initializer.
Initializer()27 bool Initializer() {
28   return CategoryRegistry::kCategoryMetadata &&
29          CategoryRegistry::kCategoryMetadata->is_valid();
30 }
31 bool g_initializer_check = Initializer();
32 
33 class TraceCategoryTest : public testing::Test {
34  public:
SetUp()35   void SetUp() override { CategoryRegistry::Initialize(); }
36 
TearDown()37   void TearDown() override { CategoryRegistry::ResetForTesting(); }
38 
GetOrCreateCategoryByName(const char * name,TraceCategory ** cat)39   static bool GetOrCreateCategoryByName(const char* name, TraceCategory** cat) {
40     static LazyInstance<Lock>::Leaky g_lock = LAZY_INSTANCE_INITIALIZER;
41     bool is_new_cat = false;
42     *cat = CategoryRegistry::GetCategoryByName(name);
43     if (!*cat) {
44       AutoLock lock(g_lock.Get());
45       is_new_cat = CategoryRegistry::GetOrCreateCategoryLocked(
46           name, [](TraceCategory*) {}, cat);
47     }
48     return is_new_cat;
49   }
50 
GetAllCategories()51   static base::span<TraceCategory> GetAllCategories() {
52     return CategoryRegistry::GetAllCategories();
53   }
54 
TestRaceThreadMain(WaitableEvent * event)55   static void TestRaceThreadMain(WaitableEvent* event) {
56     TraceCategory* cat = nullptr;
57     event->Wait();
58     GetOrCreateCategoryByName("__test_race", &cat);
59     EXPECT_NE(nullptr, cat);
60   }
61 
GetBuiltinCategoryByName(const char * category_group)62   static constexpr TraceCategory* GetBuiltinCategoryByName(
63       const char* category_group) {
64     return CategoryRegistry::GetBuiltinCategoryByName(category_group);
65   }
66 };
67 
TEST_F(TraceCategoryTest,Basic)68 TEST_F(TraceCategoryTest, Basic) {
69   ASSERT_NE(nullptr, CategoryRegistry::kCategoryMetadata);
70   ASSERT_TRUE(CategoryRegistry::kCategoryMetadata->is_valid());
71   ASSERT_FALSE(CategoryRegistry::kCategoryMetadata->is_enabled());
72 
73   // Metadata category is built-in and should create a new category.
74   TraceCategory* cat_meta = nullptr;
75   const char* kMetadataName = CategoryRegistry::kCategoryMetadata->name();
76   ASSERT_FALSE(GetOrCreateCategoryByName(kMetadataName, &cat_meta));
77   ASSERT_EQ(CategoryRegistry::kCategoryMetadata, cat_meta);
78 
79   TraceCategory* cat_1 = nullptr;
80   ASSERT_TRUE(GetOrCreateCategoryByName("__test_basic_ab", &cat_1));
81   ASSERT_FALSE(cat_1->is_enabled());
82   ASSERT_EQ(0u, cat_1->enabled_filters());
83   cat_1->set_state_flag(TraceCategory::ENABLED_FOR_RECORDING);
84   cat_1->set_state_flag(TraceCategory::ENABLED_FOR_FILTERING);
85   ASSERT_EQ(TraceCategory::ENABLED_FOR_RECORDING |
86                 TraceCategory::ENABLED_FOR_FILTERING,
87             cat_1->state());
88 
89   cat_1->set_enabled_filters(129);
90   ASSERT_EQ(129u, cat_1->enabled_filters());
91   ASSERT_EQ(cat_1, CategoryRegistry::GetCategoryByStatePtr(cat_1->state_ptr()));
92 
93   cat_1->clear_state_flag(TraceCategory::ENABLED_FOR_FILTERING);
94   ASSERT_EQ(TraceCategory::ENABLED_FOR_RECORDING, cat_1->state());
95   ASSERT_EQ(TraceCategory::ENABLED_FOR_RECORDING, *cat_1->state_ptr());
96   ASSERT_TRUE(cat_1->is_enabled());
97 
98   TraceCategory* cat_2 = nullptr;
99   ASSERT_TRUE(GetOrCreateCategoryByName("__test_basic_a", &cat_2));
100   ASSERT_FALSE(cat_2->is_enabled());
101   cat_2->set_state_flag(TraceCategory::ENABLED_FOR_RECORDING);
102 
103   TraceCategory* cat_2_copy = nullptr;
104   ASSERT_FALSE(GetOrCreateCategoryByName("__test_basic_a", &cat_2_copy));
105   ASSERT_EQ(cat_2, cat_2_copy);
106 
107   TraceCategory* cat_3 = nullptr;
108   ASSERT_TRUE(
109       GetOrCreateCategoryByName("__test_basic_ab,__test_basic_a", &cat_3));
110   ASSERT_FALSE(cat_3->is_enabled());
111   ASSERT_EQ(0u, cat_3->enabled_filters());
112 
113   int num_test_categories_seen = 0;
114   for (const TraceCategory& cat : GetAllCategories()) {
115     if (strcmp(cat.name(), kMetadataName) == 0)
116       ASSERT_TRUE(CategoryRegistry::IsMetaCategory(&cat));
117 
118     if (strncmp(cat.name(), "__test_basic_", 13) == 0) {
119       ASSERT_FALSE(CategoryRegistry::IsMetaCategory(&cat));
120       num_test_categories_seen++;
121     }
122   }
123   ASSERT_EQ(3, num_test_categories_seen);
124   ASSERT_TRUE(g_initializer_check);
125 }
126 
127 // Tries to cover the case of multiple threads creating the same category
128 // simultaneously. Should never end up with distinct entries with the same name.
TEST_F(TraceCategoryTest,ThreadRaces)129 TEST_F(TraceCategoryTest, ThreadRaces) {
130   const int kNumThreads = 32;
131   std::unique_ptr<Thread> threads[kNumThreads];
132   for (int i = 0; i < kNumThreads; i++) {
133     threads[i] = std::make_unique<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 // Tests getting trace categories by name at compile-time.
TEST_F(TraceCategoryTest,GetCategoryAtCompileTime)155 TEST_F(TraceCategoryTest, GetCategoryAtCompileTime) {
156   static_assert(GetBuiltinCategoryByName("nonexistent") == nullptr,
157                 "nonexistent found");
158 #if BUILDFLAG(IS_WIN) && defined(COMPONENT_BUILD)
159   static_assert(GetBuiltinCategoryByName("toplevel") == nullptr,
160                 "toplevel found");
161 #else
162   static_assert(GetBuiltinCategoryByName("toplevel") != nullptr,
163                 "toplevel not found");
164 #endif
165 }
166 
167 }  // namespace trace_event
168 }  // namespace base
169