OLD | NEW |
| (Empty) |
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/debug/activity_tracker.h" | |
6 | |
7 #include <memory> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/files/file.h" | |
11 #include "base/files/file_util.h" | |
12 #include "base/files/memory_mapped_file.h" | |
13 #include "base/files/scoped_temp_dir.h" | |
14 #include "base/memory/ptr_util.h" | |
15 #include "base/pending_task.h" | |
16 #include "base/synchronization/condition_variable.h" | |
17 #include "base/synchronization/lock.h" | |
18 #include "base/synchronization/spin_wait.h" | |
19 #include "base/threading/simple_thread.h" | |
20 #include "base/time/time.h" | |
21 #include "testing/gtest/include/gtest/gtest.h" | |
22 | |
23 namespace base { | |
24 namespace debug { | |
25 | |
26 namespace { | |
27 | |
28 class TestActivityTracker : public ThreadActivityTracker { | |
29 public: | |
30 TestActivityTracker(std::unique_ptr<char[]> memory, size_t mem_size) | |
31 : ThreadActivityTracker(memset(memory.get(), 0, mem_size), mem_size), | |
32 mem_segment_(std::move(memory)) {} | |
33 | |
34 ~TestActivityTracker() override {} | |
35 | |
36 private: | |
37 std::unique_ptr<char[]> mem_segment_; | |
38 }; | |
39 | |
40 } // namespace | |
41 | |
42 | |
43 class ActivityTrackerTest : public testing::Test { | |
44 public: | |
45 const int kMemorySize = 1 << 10; // 1MiB | |
46 const int kStackSize = 1 << 10; // 1KiB | |
47 | |
48 using Activity = ThreadActivityTracker::Activity; | |
49 using ActivityData = ThreadActivityTracker::ActivityData; | |
50 | |
51 ActivityTrackerTest() {} | |
52 | |
53 ~ActivityTrackerTest() override { | |
54 GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get(); | |
55 if (global_tracker) { | |
56 global_tracker->ReleaseTrackerForCurrentThreadForTesting(); | |
57 delete global_tracker; | |
58 } | |
59 } | |
60 | |
61 std::unique_ptr<ThreadActivityTracker> CreateActivityTracker() { | |
62 std::unique_ptr<char[]> memory(new char[kStackSize]); | |
63 return WrapUnique(new TestActivityTracker(std::move(memory), kStackSize)); | |
64 } | |
65 | |
66 size_t GetGlobalActiveTrackerCount() { | |
67 GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get(); | |
68 if (!global_tracker) | |
69 return 0; | |
70 return global_tracker->thread_tracker_count_.load( | |
71 std::memory_order_relaxed); | |
72 } | |
73 | |
74 size_t GetGlobalInactiveTrackerCount() { | |
75 GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get(); | |
76 if (!global_tracker) | |
77 return 0; | |
78 return global_tracker->available_memories_count_.load( | |
79 std::memory_order_relaxed); | |
80 } | |
81 | |
82 static void DoNothing() {} | |
83 }; | |
84 | |
85 TEST_F(ActivityTrackerTest, PushPopTest) { | |
86 std::unique_ptr<ThreadActivityTracker> tracker = CreateActivityTracker(); | |
87 ThreadActivityTracker::ActivitySnapshot snapshot; | |
88 | |
89 ASSERT_TRUE(tracker->Snapshot(&snapshot)); | |
90 ASSERT_EQ(0U, snapshot.activity_stack_depth); | |
91 ASSERT_EQ(0U, snapshot.activity_stack.size()); | |
92 | |
93 char origin1; | |
94 tracker->PushActivity(&origin1, ThreadActivityTracker::ACT_TASK, | |
95 ActivityData::ForTask(11)); | |
96 ASSERT_TRUE(tracker->Snapshot(&snapshot)); | |
97 ASSERT_EQ(1U, snapshot.activity_stack_depth); | |
98 ASSERT_EQ(1U, snapshot.activity_stack.size()); | |
99 EXPECT_NE(0, snapshot.activity_stack[0].time_internal); | |
100 EXPECT_EQ(ThreadActivityTracker::ACT_TASK, | |
101 snapshot.activity_stack[0].activity_type); | |
102 EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin1), | |
103 snapshot.activity_stack[0].origin_address); | |
104 EXPECT_EQ(11U, snapshot.activity_stack[0].data.task.sequence_id); | |
105 | |
106 char origin2; | |
107 char lock2; | |
108 tracker->PushActivity(&origin2, ThreadActivityTracker::ACT_LOCK, | |
109 ActivityData::ForLock(&lock2)); | |
110 ASSERT_TRUE(tracker->Snapshot(&snapshot)); | |
111 ASSERT_EQ(2U, snapshot.activity_stack_depth); | |
112 ASSERT_EQ(2U, snapshot.activity_stack.size()); | |
113 EXPECT_LE(snapshot.activity_stack[0].time_internal, | |
114 snapshot.activity_stack[1].time_internal); | |
115 EXPECT_EQ(ThreadActivityTracker::ACT_LOCK, | |
116 snapshot.activity_stack[1].activity_type); | |
117 EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin2), | |
118 snapshot.activity_stack[1].origin_address); | |
119 EXPECT_EQ(reinterpret_cast<uintptr_t>(&lock2), | |
120 snapshot.activity_stack[1].data.lock.lock_address); | |
121 | |
122 tracker->PopActivity(); | |
123 ASSERT_TRUE(tracker->Snapshot(&snapshot)); | |
124 ASSERT_EQ(1U, snapshot.activity_stack_depth); | |
125 ASSERT_EQ(1U, snapshot.activity_stack.size()); | |
126 EXPECT_EQ(ThreadActivityTracker::ACT_TASK, | |
127 snapshot.activity_stack[0].activity_type); | |
128 EXPECT_EQ(reinterpret_cast<uintptr_t>(&origin1), | |
129 snapshot.activity_stack[0].origin_address); | |
130 EXPECT_EQ(11U, snapshot.activity_stack[0].data.task.sequence_id); | |
131 | |
132 tracker->PopActivity(); | |
133 ASSERT_TRUE(tracker->Snapshot(&snapshot)); | |
134 ASSERT_EQ(0U, snapshot.activity_stack_depth); | |
135 ASSERT_EQ(0U, snapshot.activity_stack.size()); | |
136 } | |
137 | |
138 TEST_F(ActivityTrackerTest, ScopedTaskTest) { | |
139 GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3); | |
140 | |
141 ThreadActivityTracker* tracker = | |
142 GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread(); | |
143 ThreadActivityTracker::ActivitySnapshot snapshot; | |
144 | |
145 ASSERT_TRUE(tracker->Snapshot(&snapshot)); | |
146 ASSERT_EQ(0U, snapshot.activity_stack_depth); | |
147 ASSERT_EQ(0U, snapshot.activity_stack.size()); | |
148 | |
149 { | |
150 PendingTask task1(FROM_HERE, base::Bind(&DoNothing)); | |
151 ScopedTaskRunActivity activity1(task1); | |
152 | |
153 ASSERT_TRUE(tracker->Snapshot(&snapshot)); | |
154 ASSERT_EQ(1U, snapshot.activity_stack_depth); | |
155 ASSERT_EQ(1U, snapshot.activity_stack.size()); | |
156 EXPECT_EQ(ThreadActivityTracker::ACT_TASK, | |
157 snapshot.activity_stack[0].activity_type); | |
158 | |
159 { | |
160 PendingTask task2(FROM_HERE, base::Bind(&DoNothing)); | |
161 ScopedTaskRunActivity activity2(task2); | |
162 | |
163 ASSERT_TRUE(tracker->Snapshot(&snapshot)); | |
164 ASSERT_EQ(2U, snapshot.activity_stack_depth); | |
165 ASSERT_EQ(2U, snapshot.activity_stack.size()); | |
166 EXPECT_EQ(ThreadActivityTracker::ACT_TASK, | |
167 snapshot.activity_stack[1].activity_type); | |
168 } | |
169 | |
170 ASSERT_TRUE(tracker->Snapshot(&snapshot)); | |
171 ASSERT_EQ(1U, snapshot.activity_stack_depth); | |
172 ASSERT_EQ(1U, snapshot.activity_stack.size()); | |
173 EXPECT_EQ(ThreadActivityTracker::ACT_TASK, | |
174 snapshot.activity_stack[0].activity_type); | |
175 } | |
176 | |
177 ASSERT_TRUE(tracker->Snapshot(&snapshot)); | |
178 ASSERT_EQ(0U, snapshot.activity_stack_depth); | |
179 ASSERT_EQ(0U, snapshot.activity_stack.size()); | |
180 } | |
181 | |
182 TEST_F(ActivityTrackerTest, CreateWithFileTest) { | |
183 const char temp_name[] = "CreateWithFileTest"; | |
184 ScopedTempDir temp_dir; | |
185 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | |
186 FilePath temp_file = temp_dir.path().AppendASCII(temp_name); | |
187 const size_t temp_size = 64 << 10; // 64 KiB | |
188 | |
189 // Create a global tracker on a new file. | |
190 ASSERT_FALSE(PathExists(temp_file)); | |
191 GlobalActivityTracker::CreateWithFile(temp_file, temp_size, 0, "foo", 3); | |
192 GlobalActivityTracker* global = GlobalActivityTracker::Get(); | |
193 EXPECT_EQ(std::string("foo"), global->allocator()->Name()); | |
194 global->ReleaseTrackerForCurrentThreadForTesting(); | |
195 delete global; | |
196 | |
197 // Create a global tracker over an existing file, replacing it. If the | |
198 // replacement doesn't work, the name will remain as it was first created. | |
199 ASSERT_TRUE(PathExists(temp_file)); | |
200 GlobalActivityTracker::CreateWithFile(temp_file, temp_size, 0, "bar", 3); | |
201 global = GlobalActivityTracker::Get(); | |
202 EXPECT_EQ(std::string("bar"), global->allocator()->Name()); | |
203 global->ReleaseTrackerForCurrentThreadForTesting(); | |
204 delete global; | |
205 } | |
206 | |
207 | |
208 // GlobalActivityTracker tests below. | |
209 | |
210 class SimpleActivityThread : public SimpleThread { | |
211 public: | |
212 SimpleActivityThread(const std::string& name, | |
213 const void* origin, | |
214 ThreadActivityTracker::ActivityType activity, | |
215 const ThreadActivityTracker::ActivityData& data) | |
216 : SimpleThread(name, Options()), | |
217 origin_(origin), | |
218 activity_(activity), | |
219 data_(data), | |
220 exit_condition_(&lock_) {} | |
221 | |
222 ~SimpleActivityThread() override {} | |
223 | |
224 void Run() override { | |
225 GlobalActivityTracker::Get() | |
226 ->GetOrCreateTrackerForCurrentThread() | |
227 ->PushActivity(origin_, activity_, data_); | |
228 | |
229 { | |
230 AutoLock auto_lock(lock_); | |
231 ready_ = true; | |
232 while (!exit_) | |
233 exit_condition_.Wait(); | |
234 } | |
235 | |
236 GlobalActivityTracker::Get() | |
237 ->GetOrCreateTrackerForCurrentThread() | |
238 ->PopActivity(); | |
239 } | |
240 | |
241 void Exit() { | |
242 AutoLock auto_lock(lock_); | |
243 exit_ = true; | |
244 exit_condition_.Signal(); | |
245 } | |
246 | |
247 void WaitReady() { | |
248 SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(ready_); | |
249 } | |
250 | |
251 private: | |
252 const void* origin_; | |
253 ThreadActivityTracker::ActivityType activity_; | |
254 ThreadActivityTracker::ActivityData data_; | |
255 | |
256 bool ready_ = false; | |
257 bool exit_ = false; | |
258 Lock lock_; | |
259 ConditionVariable exit_condition_; | |
260 | |
261 DISALLOW_COPY_AND_ASSIGN(SimpleActivityThread); | |
262 }; | |
263 | |
264 TEST_F(ActivityTrackerTest, ThreadDeathTest) { | |
265 GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3); | |
266 GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread(); | |
267 const size_t starting_active = GetGlobalActiveTrackerCount(); | |
268 const size_t starting_inactive = GetGlobalInactiveTrackerCount(); | |
269 | |
270 SimpleActivityThread t1("t1", nullptr, ThreadActivityTracker::ACT_TASK, | |
271 ThreadActivityTracker::ActivityData::ForTask(11)); | |
272 t1.Start(); | |
273 t1.WaitReady(); | |
274 EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount()); | |
275 EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount()); | |
276 | |
277 t1.Exit(); | |
278 t1.Join(); | |
279 EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount()); | |
280 EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount()); | |
281 | |
282 // Start another thread and ensure it re-uses the existing memory. | |
283 | |
284 SimpleActivityThread t2("t2", nullptr, ThreadActivityTracker::ACT_TASK, | |
285 ThreadActivityTracker::ActivityData::ForTask(22)); | |
286 t2.Start(); | |
287 t2.WaitReady(); | |
288 EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount()); | |
289 EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount()); | |
290 | |
291 t2.Exit(); | |
292 t2.Join(); | |
293 EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount()); | |
294 EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount()); | |
295 } | |
296 | |
297 } // namespace debug | |
298 } // namespace base | |
OLD | NEW |