1 // Copyright 2020 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 "base/threading/hang_watcher.h"
6 #include <atomic>
7 #include <memory>
8
9 #include "base/barrier_closure.h"
10 #include "base/functional/bind.h"
11 #include "base/functional/callback.h"
12 #include "base/functional/callback_helpers.h"
13 #include "base/memory/raw_ptr.h"
14 #include "base/metrics/field_trial_params.h"
15 #include "base/run_loop.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/synchronization/lock.h"
18 #include "base/synchronization/waitable_event.h"
19 #include "base/test/bind.h"
20 #include "base/test/metrics/histogram_tester.h"
21 #include "base/test/power_monitor_test.h"
22 #include "base/test/scoped_feature_list.h"
23 #include "base/test/simple_test_tick_clock.h"
24 #include "base/test/task_environment.h"
25 #include "base/test/test_timeouts.h"
26 #include "base/threading/platform_thread.h"
27 #include "base/threading/thread_checker.h"
28 #include "base/threading/threading_features.h"
29 #include "base/time/tick_clock.h"
30 #include "base/time/time.h"
31 #include "build/build_config.h"
32 #include "testing/gmock/include/gmock/gmock.h"
33 #include "testing/gtest/include/gtest/gtest.h"
34 #include "third_party/abseil-cpp/absl/types/optional.h"
35
36 using testing::ElementsAre;
37 using testing::IsEmpty;
38
39 namespace base {
40 namespace {
41
42 // Use with a FeatureList to activate crash dumping for threads marked as
43 // threadpool threads.
44 const std::vector<base::test::FeatureRefAndParams> kFeatureAndParams{
45 {base::kEnableHangWatcher, {{"ui_thread_log_level", "2"}}}};
46
47 // Use this value to mark things very far off in the future. Adding this
48 // to TimeTicks::Now() gives a point that will never be reached during the
49 // normal execution of a test.
50 constexpr TimeDelta kVeryLongDelta{base::Days(365)};
51
52 // A relatively small time delta to ensure ordering of hung threads list.
53 constexpr TimeDelta kSmallCPUQuantum{base::Milliseconds(1)};
54
55 constexpr uint64_t kArbitraryDeadline = 0x0000C0FFEEC0FFEEu;
56 constexpr uint64_t kAllOnes = 0xFFFFFFFFFFFFFFFFu;
57 constexpr uint64_t kAllZeros = 0x0000000000000000u;
58 constexpr uint64_t kOnesThenZeroes = 0xAAAAAAAAAAAAAAAAu;
59 constexpr uint64_t kZeroesThenOnes = 0x5555555555555555u;
60
61 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
62 class HangWatcherEnabledInZygoteChildTest
63 : public testing::TestWithParam<std::tuple<bool, bool>> {
64 public:
HangWatcherEnabledInZygoteChildTest()65 HangWatcherEnabledInZygoteChildTest() {
66 std::vector<base::test::FeatureRefAndParams> enabled_features =
67 kFeatureAndParams;
68 std::vector<test::FeatureRef> disabled_features;
69 if (std::get<0>(GetParam())) {
70 enabled_features.push_back(test::FeatureRefAndParams(
71 base::kEnableHangWatcherInZygoteChildren, {}));
72 } else {
73 disabled_features.push_back(base::kEnableHangWatcherInZygoteChildren);
74 }
75 feature_list_.InitWithFeaturesAndParameters(enabled_features,
76 disabled_features);
77 HangWatcher::InitializeOnMainThread(
78 HangWatcher::ProcessType::kUtilityProcess,
79 /*is_zygote_child=*/std::get<1>(GetParam()));
80 }
81
TearDown()82 void TearDown() override { HangWatcher::UnitializeOnMainThreadForTesting(); }
83
84 HangWatcherEnabledInZygoteChildTest(
85 const HangWatcherEnabledInZygoteChildTest& other) = delete;
86 HangWatcherEnabledInZygoteChildTest& operator=(
87 const HangWatcherEnabledInZygoteChildTest& other) = delete;
88
89 protected:
90 base::test::ScopedFeatureList feature_list_;
91 };
92
TEST_P(HangWatcherEnabledInZygoteChildTest,IsEnabled)93 TEST_P(HangWatcherEnabledInZygoteChildTest, IsEnabled) {
94 // If the kEnableHangWatcherInZygoteChildren feature is disabled and
95 // InitializeOnMainThread is called with is_zygote_child==true, IsEnabled()
96 // should return false. It should return true in all other situations.
97 ASSERT_EQ(std::get<0>(GetParam()) || !std::get<1>(GetParam()),
98 HangWatcher::IsEnabled());
99 }
100
101 INSTANTIATE_TEST_SUITE_P(HangWatcherZygoteTest,
102 HangWatcherEnabledInZygoteChildTest,
103 testing::Combine(testing::Bool(), testing::Bool()));
104 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
105
106 // Waits on provided WaitableEvent before executing and signals when done.
107 class BlockingThread : public DelegateSimpleThread::Delegate {
108 public:
BlockingThread(base::WaitableEvent * unblock_thread,base::TimeDelta timeout)109 explicit BlockingThread(base::WaitableEvent* unblock_thread,
110 base::TimeDelta timeout)
111 : thread_(this, "BlockingThread"),
112 unblock_thread_(unblock_thread),
113 timeout_(timeout) {}
114
115 ~BlockingThread() override = default;
116
Run()117 void Run() override {
118 // (Un)Register the thread here instead of in ctor/dtor so that the action
119 // happens on the right thread.
120 base::ScopedClosureRunner unregister_closure =
121 base::HangWatcher::RegisterThread(
122 base::HangWatcher::ThreadType::kMainThread);
123
124 WatchHangsInScope scope(timeout_);
125 wait_until_entered_scope_.Signal();
126
127 unblock_thread_->Wait();
128 run_event_.Signal();
129 }
130
IsDone()131 bool IsDone() { return run_event_.IsSignaled(); }
132
StartAndWaitForScopeEntered()133 void StartAndWaitForScopeEntered() {
134 thread_.Start();
135 // Block until this thread registered itself for hang watching and has
136 // entered a WatchHangsInScope.
137 wait_until_entered_scope_.Wait();
138 }
139
Join()140 void Join() { thread_.Join(); }
141
GetId()142 PlatformThreadId GetId() { return thread_.tid(); }
143
144 private:
145 base::DelegateSimpleThread thread_;
146
147 // Will be signaled once the thread is properly registered for watching and
148 // the WatchHangsInScope has been entered.
149 WaitableEvent wait_until_entered_scope_;
150
151 // Will be signaled once ThreadMain has run.
152 WaitableEvent run_event_;
153
154 const raw_ptr<base::WaitableEvent> unblock_thread_;
155
156 base::TimeDelta timeout_;
157 };
158
159 class HangWatcherTest : public testing::Test {
160 public:
161 const base::TimeDelta kTimeout = base::Seconds(10);
162 const base::TimeDelta kHangTime = kTimeout + base::Seconds(1);
163
HangWatcherTest()164 HangWatcherTest() {
165 feature_list_.InitWithFeaturesAndParameters(kFeatureAndParams, {});
166 HangWatcher::InitializeOnMainThread(
167 HangWatcher::ProcessType::kBrowserProcess, false);
168
169 hang_watcher_.SetAfterMonitorClosureForTesting(base::BindRepeating(
170 &WaitableEvent::Signal, base::Unretained(&monitor_event_)));
171
172 hang_watcher_.SetOnHangClosureForTesting(base::BindRepeating(
173 &WaitableEvent::Signal, base::Unretained(&hang_event_)));
174
175 // We're not testing the monitoring loop behavior in this test so we want to
176 // trigger monitoring manually.
177 hang_watcher_.SetMonitoringPeriodForTesting(kVeryLongDelta);
178
179 // Start the monitoring loop.
180 hang_watcher_.Start();
181 }
182
TearDown()183 void TearDown() override { HangWatcher::UnitializeOnMainThreadForTesting(); }
184
185 HangWatcherTest(const HangWatcherTest& other) = delete;
186 HangWatcherTest& operator=(const HangWatcherTest& other) = delete;
187
188 protected:
189 // Used to wait for monitoring. Will be signaled by the HangWatcher thread and
190 // so needs to outlive it.
191 WaitableEvent monitor_event_;
192
193 // Signaled from the HangWatcher thread when a hang is detected. Needs to
194 // outlive the HangWatcher thread.
195 WaitableEvent hang_event_;
196
197 base::test::ScopedFeatureList feature_list_;
198
199 // Used exclusively for MOCK_TIME. No tasks will be run on the environment.
200 // Single threaded to avoid ThreadPool WorkerThreads registering.
201 test::SingleThreadTaskEnvironment task_environment_{
202 test::TaskEnvironment::TimeSource::MOCK_TIME};
203
204 // This must be declared last (after task_environment_, for example) so that
205 // the watcher thread is joined before objects like the mock timer are
206 // destroyed, causing racy crashes.
207 HangWatcher hang_watcher_;
208 };
209
210 class HangWatcherBlockingThreadTest : public HangWatcherTest {
211 public:
HangWatcherBlockingThreadTest()212 HangWatcherBlockingThreadTest() : thread_(&unblock_thread_, kTimeout) {}
213
214 HangWatcherBlockingThreadTest(const HangWatcherBlockingThreadTest& other) =
215 delete;
216 HangWatcherBlockingThreadTest& operator=(
217 const HangWatcherBlockingThreadTest& other) = delete;
218
219 protected:
JoinThread()220 void JoinThread() {
221 unblock_thread_.Signal();
222
223 // Thread is joinable since we signaled |unblock_thread_|.
224 thread_.Join();
225
226 // If thread is done then it signaled.
227 ASSERT_TRUE(thread_.IsDone());
228 }
229
StartBlockedThread()230 void StartBlockedThread() {
231 // Thread has not run yet.
232 ASSERT_FALSE(thread_.IsDone());
233
234 // Start the thread. It will block since |unblock_thread_| was not
235 // signaled yet.
236 thread_.StartAndWaitForScopeEntered();
237
238 // Thread registration triggered a call to HangWatcher::Monitor() which
239 // signaled |monitor_event_|. Reset it so it's ready for waiting later on.
240 monitor_event_.Reset();
241 }
242
MonitorHangs()243 void MonitorHangs() {
244 // HangWatcher::Monitor() should not be set which would mean a call to
245 // HangWatcher::Monitor() happened and was unacounted for.
246 // ASSERT_FALSE(monitor_event_.IsSignaled());
247
248 // Trigger a monitoring on HangWatcher thread and verify results.
249 hang_watcher_.SignalMonitorEventForTesting();
250 monitor_event_.Wait();
251 }
252
253 // Used to unblock the monitored thread. Signaled from the test main thread.
254 WaitableEvent unblock_thread_;
255
256 BlockingThread thread_;
257 };
258 } // namespace
259
TEST_F(HangWatcherTest,InvalidatingExpectationsPreventsCapture)260 TEST_F(HangWatcherTest, InvalidatingExpectationsPreventsCapture) {
261 // Register the main test thread for hang watching.
262 auto unregister_thread_closure =
263 HangWatcher::RegisterThread(base::HangWatcher::ThreadType::kMainThread);
264
265 // Create a hang.
266 WatchHangsInScope expires_instantly(base::TimeDelta{});
267 task_environment_.FastForwardBy(kHangTime);
268
269 // de-activate hang watching,
270 base::HangWatcher::InvalidateActiveExpectations();
271
272 // Trigger a monitoring on HangWatcher thread and verify results.
273 // Hang is not detected.
274 hang_watcher_.SignalMonitorEventForTesting();
275 monitor_event_.Wait();
276 ASSERT_FALSE(hang_event_.IsSignaled());
277 }
278
TEST_F(HangWatcherTest,MultipleInvalidateExpectationsDoNotCancelOut)279 TEST_F(HangWatcherTest, MultipleInvalidateExpectationsDoNotCancelOut) {
280 // Register the main test thread for hang watching.
281 auto unregister_thread_closure =
282 HangWatcher::RegisterThread(base::HangWatcher::ThreadType::kMainThread);
283
284 // Create a hang.
285 WatchHangsInScope expires_instantly(base::TimeDelta{});
286 task_environment_.FastForwardBy(kHangTime);
287
288 // de-activate hang watching,
289 base::HangWatcher::InvalidateActiveExpectations();
290
291 // Redundently de-activate hang watching.
292 base::HangWatcher::InvalidateActiveExpectations();
293
294 // Trigger a monitoring on HangWatcher thread and verify results.
295 // Hang is not detected.
296 hang_watcher_.SignalMonitorEventForTesting();
297 monitor_event_.Wait();
298 ASSERT_FALSE(hang_event_.IsSignaled());
299 }
300
TEST_F(HangWatcherTest,NewInnerWatchHangsInScopeAfterInvalidationDetectsHang)301 TEST_F(HangWatcherTest, NewInnerWatchHangsInScopeAfterInvalidationDetectsHang) {
302 // Register the main test thread for hang watching.
303 auto unregister_thread_closure =
304 HangWatcher::RegisterThread(base::HangWatcher::ThreadType::kMainThread);
305
306 WatchHangsInScope expires_instantly(base::TimeDelta{});
307 task_environment_.FastForwardBy(kHangTime);
308
309 // De-activate hang watching.
310 base::HangWatcher::InvalidateActiveExpectations();
311
312 {
313 WatchHangsInScope also_expires_instantly(base::TimeDelta{});
314 task_environment_.FastForwardBy(kHangTime);
315
316 // Trigger a monitoring on HangWatcher thread and verify results.
317 hang_watcher_.SignalMonitorEventForTesting();
318 monitor_event_.Wait();
319
320 // Hang is detected since the new WatchHangsInScope temporarily
321 // re-activated hang_watching.
322 monitor_event_.Wait();
323 ASSERT_TRUE(hang_event_.IsSignaled());
324 }
325
326 // Reset to attempt capture again.
327 monitor_event_.Reset();
328 hang_event_.Reset();
329
330 // Trigger a monitoring on HangWatcher thread and verify results.
331 hang_watcher_.SignalMonitorEventForTesting();
332 monitor_event_.Wait();
333
334 // Hang is not detected since execution is back to being covered by
335 // |expires_instantly| for which expectations were invalidated.
336 monitor_event_.Wait();
337 ASSERT_FALSE(hang_event_.IsSignaled());
338 }
339
TEST_F(HangWatcherTest,NewSeparateWatchHangsInScopeAfterInvalidationDetectsHang)340 TEST_F(HangWatcherTest,
341 NewSeparateWatchHangsInScopeAfterInvalidationDetectsHang) {
342 // Register the main test thread for hang watching.
343 auto unregister_thread_closure =
344 HangWatcher::RegisterThread(base::HangWatcher::ThreadType::kMainThread);
345
346 {
347 WatchHangsInScope expires_instantly(base::TimeDelta{});
348 task_environment_.FastForwardBy(kHangTime);
349
350 // De-activate hang watching.
351 base::HangWatcher::InvalidateActiveExpectations();
352 }
353
354 WatchHangsInScope also_expires_instantly(base::TimeDelta{});
355 task_environment_.FastForwardBy(kHangTime);
356
357 // Trigger a monitoring on HangWatcher thread and verify results.
358 hang_watcher_.SignalMonitorEventForTesting();
359 monitor_event_.Wait();
360
361 // Hang is detected since the new WatchHangsInScope did not have its
362 // expectations invalidated.
363 monitor_event_.Wait();
364 ASSERT_TRUE(hang_event_.IsSignaled());
365 }
366
367 // Test that invalidating expectations from inner WatchHangsInScope will also
368 // prevent hang detection in outer scopes.
TEST_F(HangWatcherTest,ScopeDisabledObjectInnerScope)369 TEST_F(HangWatcherTest, ScopeDisabledObjectInnerScope) {
370 // Register the main test thread for hang watching.
371 auto unregister_thread_closure =
372 HangWatcher::RegisterThread(base::HangWatcher::ThreadType::kMainThread);
373
374 // Start a WatchHangsInScope that expires right away. Then advance
375 // time to make sure no hang is detected.
376 WatchHangsInScope expires_instantly(base::TimeDelta{});
377 task_environment_.FastForwardBy(kHangTime);
378 {
379 WatchHangsInScope also_expires_instantly(base::TimeDelta{});
380
381 // De-activate hang watching.
382 base::HangWatcher::InvalidateActiveExpectations();
383 task_environment_.FastForwardBy(kHangTime);
384 }
385
386 // Trigger a monitoring on HangWatcher thread and verify results.
387 hang_watcher_.SignalMonitorEventForTesting();
388 monitor_event_.Wait();
389
390 // Hang is ignored since it concerns a scope for which one of the inner scope
391 // was ignored.
392 ASSERT_FALSE(hang_event_.IsSignaled());
393 }
394
TEST_F(HangWatcherTest,NewScopeAfterDisabling)395 TEST_F(HangWatcherTest, NewScopeAfterDisabling) {
396 // Register the main test thread for hang watching.
397 auto unregister_thread_closure =
398 HangWatcher::RegisterThread(base::HangWatcher::ThreadType::kMainThread);
399
400 // Start a WatchHangsInScope that expires right away. Then advance
401 // time to make sure no hang is detected.
402 WatchHangsInScope expires_instantly(base::TimeDelta{});
403 task_environment_.FastForwardBy(kHangTime);
404 {
405 WatchHangsInScope also_expires_instantly(base::TimeDelta{});
406
407 // De-activate hang watching.
408 base::HangWatcher::InvalidateActiveExpectations();
409 task_environment_.FastForwardBy(kHangTime);
410 }
411
412 // New scope for which expecations are never invalidated.
413 WatchHangsInScope also_expires_instantly(base::TimeDelta{});
414 task_environment_.FastForwardBy(kHangTime);
415
416 // Trigger a monitoring on HangWatcher thread and verify results.
417 hang_watcher_.SignalMonitorEventForTesting();
418 monitor_event_.Wait();
419
420 // Hang is detected because it's unrelated to the hangs that were disabled.
421 ASSERT_TRUE(hang_event_.IsSignaled());
422 }
423
TEST_F(HangWatcherTest,NestedScopes)424 TEST_F(HangWatcherTest, NestedScopes) {
425 // Create a state object for the test thread since this test is single
426 // threaded.
427 auto current_hang_watch_state =
428 base::internal::HangWatchState::CreateHangWatchStateForCurrentThread(
429 HangWatcher::ThreadType::kMainThread);
430
431 ASSERT_FALSE(current_hang_watch_state->IsOverDeadline());
432 base::TimeTicks original_deadline = current_hang_watch_state->GetDeadline();
433
434 constexpr base::TimeDelta kFirstTimeout(base::Milliseconds(500));
435 base::TimeTicks first_deadline = base::TimeTicks::Now() + kFirstTimeout;
436
437 constexpr base::TimeDelta kSecondTimeout(base::Milliseconds(250));
438 base::TimeTicks second_deadline = base::TimeTicks::Now() + kSecondTimeout;
439
440 // At this point we have not set any timeouts.
441 {
442 // Create a first timeout which is more restrictive than the default.
443 WatchHangsInScope first_scope(kFirstTimeout);
444
445 // We are on mock time. There is no time advancement and as such no hangs.
446 ASSERT_FALSE(current_hang_watch_state->IsOverDeadline());
447 ASSERT_EQ(current_hang_watch_state->GetDeadline(), first_deadline);
448 {
449 // Set a yet more restrictive deadline. Still no hang.
450 WatchHangsInScope second_scope(kSecondTimeout);
451 ASSERT_FALSE(current_hang_watch_state->IsOverDeadline());
452 ASSERT_EQ(current_hang_watch_state->GetDeadline(), second_deadline);
453 }
454 // First deadline we set should be restored.
455 ASSERT_FALSE(current_hang_watch_state->IsOverDeadline());
456 ASSERT_EQ(current_hang_watch_state->GetDeadline(), first_deadline);
457 }
458
459 // Original deadline should now be restored.
460 ASSERT_FALSE(current_hang_watch_state->IsOverDeadline());
461 ASSERT_EQ(current_hang_watch_state->GetDeadline(), original_deadline);
462 }
463
TEST_F(HangWatcherBlockingThreadTest,HistogramsLoggedOnHang)464 TEST_F(HangWatcherBlockingThreadTest, HistogramsLoggedOnHang) {
465 base::HistogramTester histogram_tester;
466 StartBlockedThread();
467
468 // Simulate hang.
469 task_environment_.FastForwardBy(kHangTime);
470
471 // First monitoring catches the hang and emits the histogram.
472 MonitorHangs();
473 EXPECT_THAT(histogram_tester.GetAllSamples("HangWatcher.IsThreadHung."
474 "BrowserProcess.UIThread"),
475 ElementsAre(base::Bucket(true, /*count=*/1)));
476
477 // Reset to attempt capture again.
478 hang_event_.Reset();
479 monitor_event_.Reset();
480
481 // Hang is logged again even if it would not trigger a crash dump.
482 MonitorHangs();
483 EXPECT_THAT(histogram_tester.GetAllSamples("HangWatcher.IsThreadHung."
484 "BrowserProcess.UIThread"),
485 ElementsAre(base::Bucket(true, /*count=*/2)));
486
487 // Thread types that are not monitored should not get any samples.
488 EXPECT_THAT(histogram_tester.GetAllSamples("HangWatcher.IsThreadHung."
489 "BrowserProcess.IOThread"),
490 IsEmpty());
491 JoinThread();
492 }
493
TEST_F(HangWatcherBlockingThreadTest,HistogramsLoggedWithoutHangs)494 TEST_F(HangWatcherBlockingThreadTest, HistogramsLoggedWithoutHangs) {
495 base::HistogramTester histogram_tester;
496 StartBlockedThread();
497
498 // No hang to catch so nothing is recorded.
499 MonitorHangs();
500 ASSERT_FALSE(hang_event_.IsSignaled());
501
502 // A thread of type ThreadForTesting was monitored but didn't hang. This is
503 // logged.
504 EXPECT_THAT(histogram_tester.GetAllSamples("HangWatcher.IsThreadHung."
505 "BrowserProcess.UIThread"),
506 ElementsAre(base::Bucket(false, /*count=*/1)));
507
508 // Thread types that are not monitored should not get any samples.
509 EXPECT_THAT(histogram_tester.GetAllSamples("HangWatcher.IsThreadHung."
510 "BrowserProcess.IOThread"),
511 IsEmpty());
512 JoinThread();
513 }
514
TEST_F(HangWatcherBlockingThreadTest,Hang)515 TEST_F(HangWatcherBlockingThreadTest, Hang) {
516 StartBlockedThread();
517
518 // Simulate hang.
519 task_environment_.FastForwardBy(kHangTime);
520
521 // First monitoring catches and records the hang.
522 MonitorHangs();
523 ASSERT_TRUE(hang_event_.IsSignaled());
524
525 JoinThread();
526 }
527
TEST_F(HangWatcherBlockingThreadTest,HangAlreadyRecorded)528 TEST_F(HangWatcherBlockingThreadTest, HangAlreadyRecorded) {
529 StartBlockedThread();
530
531 // Simulate hang.
532 task_environment_.FastForwardBy(kHangTime);
533
534 // First monitoring catches and records the hang.
535 MonitorHangs();
536 ASSERT_TRUE(hang_event_.IsSignaled());
537
538 // Reset to attempt capture again.
539 hang_event_.Reset();
540 monitor_event_.Reset();
541
542 // Second monitoring does not record because a hang that was already recorded
543 // is still live.
544 MonitorHangs();
545 ASSERT_FALSE(hang_event_.IsSignaled());
546
547 JoinThread();
548 }
549
TEST_F(HangWatcherBlockingThreadTest,NoHang)550 TEST_F(HangWatcherBlockingThreadTest, NoHang) {
551 StartBlockedThread();
552
553 // No hang to catch so nothing is recorded.
554 MonitorHangs();
555 ASSERT_FALSE(hang_event_.IsSignaled());
556
557 JoinThread();
558 }
559
560 namespace {
561 class HangWatcherSnapshotTest : public testing::Test {
562 public:
SetUp()563 void SetUp() override {
564 feature_list_.InitWithFeaturesAndParameters(kFeatureAndParams, {});
565 HangWatcher::InitializeOnMainThread(
566 HangWatcher::ProcessType::kBrowserProcess, false);
567
568 // The monitoring loop behavior is not verified in this test so we want to
569 // trigger monitoring manually.
570 hang_watcher_.SetMonitoringPeriodForTesting(kVeryLongDelta);
571 }
572
TearDown()573 void TearDown() override { HangWatcher::UnitializeOnMainThreadForTesting(); }
574
575 HangWatcherSnapshotTest() = default;
576 HangWatcherSnapshotTest(const HangWatcherSnapshotTest& other) = delete;
577 HangWatcherSnapshotTest& operator=(const HangWatcherSnapshotTest& other) =
578 delete;
579
580 protected:
TriggerMonitorAndWaitForCompletion()581 void TriggerMonitorAndWaitForCompletion() {
582 monitor_event_.Reset();
583 hang_watcher_.SignalMonitorEventForTesting();
584 monitor_event_.Wait();
585 }
586
587 // Verify that a capture takes place and that at the time of the capture the
588 // list of hung thread ids is correct.
TestIDList(const std::string & id_list)589 void TestIDList(const std::string& id_list) {
590 list_of_hung_thread_ids_during_capture_ = id_list;
591 task_environment_.AdvanceClock(kSmallCPUQuantum);
592 TriggerMonitorAndWaitForCompletion();
593 ASSERT_EQ(++reference_capture_count_, hang_capture_count_);
594 }
595
596 // Verify that even if hang monitoring takes place no hangs are detected.
ExpectNoCapture()597 void ExpectNoCapture() {
598 int old_capture_count = hang_capture_count_;
599 task_environment_.AdvanceClock(kSmallCPUQuantum);
600 TriggerMonitorAndWaitForCompletion();
601 ASSERT_EQ(old_capture_count, hang_capture_count_);
602 }
603
ConcatenateThreadIds(const std::vector<base::PlatformThreadId> & ids) const604 std::string ConcatenateThreadIds(
605 const std::vector<base::PlatformThreadId>& ids) const {
606 std::string result;
607 constexpr char kSeparator{'|'};
608
609 for (PlatformThreadId id : ids) {
610 result += base::NumberToString(id) + kSeparator;
611 }
612
613 return result;
614 }
615
616 // Will be signaled once monitoring took place. Marks the end of the test.
617 WaitableEvent monitor_event_;
618
619 const PlatformThreadId test_thread_id_ = PlatformThread::CurrentId();
620
621 // This is written to by the test main thread and read from the hang watching
622 // thread. It does not need to be protected because access to it is
623 // synchronized by always setting before triggering the execution of the
624 // reading code through HangWatcher::SignalMonitorEventForTesting().
625 std::string list_of_hung_thread_ids_during_capture_;
626
627 // This is written to by from the hang watching thread and read the test main
628 // thread. It does not need to be protected because access to it is
629 // synchronized by always reading after monitor_event_ has been signaled.
630 int hang_capture_count_ = 0;
631
632 // Increases at the same time as |hang_capture_count_| to test that capture
633 // actually took place.
634 int reference_capture_count_ = 0;
635
636 std::string seconds_since_last_power_resume_crash_key_;
637
638 base::test::ScopedFeatureList feature_list_;
639
640 // Used exclusively for MOCK_TIME.
641 test::SingleThreadTaskEnvironment task_environment_{
642 test::TaskEnvironment::TimeSource::MOCK_TIME};
643
644 HangWatcher hang_watcher_;
645 };
646 } // namespace
647
648 // Verify that the hang capture fails when marking a thread for blocking fails.
649 // This simulates a WatchHangsInScope completing between the time the hang
650 // was dected and the time it is recorded which would create a non-actionable
651 // report.
TEST_F(HangWatcherSnapshotTest,NonActionableReport)652 TEST_F(HangWatcherSnapshotTest, NonActionableReport) {
653 hang_watcher_.SetOnHangClosureForTesting(
654 base::BindLambdaForTesting([this]() { ++hang_capture_count_; }));
655 hang_watcher_.SetAfterMonitorClosureForTesting(
656 base::BindLambdaForTesting([this]() { monitor_event_.Signal(); }));
657
658 hang_watcher_.Start();
659
660 // Register the main test thread for hang watching.
661 auto unregister_thread_closure =
662 HangWatcher::RegisterThread(base::HangWatcher::ThreadType::kMainThread);
663 {
664 // Start a WatchHangsInScope that expires right away. Ensures that
665 // the first monitor will detect a hang.
666 WatchHangsInScope expires_instantly(base::TimeDelta{});
667
668 internal::HangWatchState* current_hang_watch_state =
669 internal::HangWatchState::GetHangWatchStateForCurrentThread();
670
671 // Simulate the deadline changing concurrently during the capture. This
672 // makes the capture fail since marking of the deadline fails.
673 ASSERT_NE(current_hang_watch_state->GetDeadline(),
674 base::TimeTicks::FromInternalValue(kArbitraryDeadline));
675 current_hang_watch_state->GetHangWatchDeadlineForTesting()
676 ->SetSwitchBitsClosureForTesting(
677 base::BindLambdaForTesting([]() { return kArbitraryDeadline; }));
678
679 ExpectNoCapture();
680
681 // Marking failed.
682 ASSERT_FALSE(current_hang_watch_state->IsFlagSet(
683 internal::HangWatchDeadline::Flag::kShouldBlockOnHang));
684
685 current_hang_watch_state->GetHangWatchDeadlineForTesting()
686 ->ResetSwitchBitsClosureForTesting();
687 }
688 }
689
690 // TODO(crbug.com/1223033): On MAC, the base::PlatformThread::CurrentId(...)
691 // should return the system wide IDs. The HungThreadIDs test fails because the
692 // reported process ids do not match.
693 #if BUILDFLAG(IS_MAC)
694 #define MAYBE_HungThreadIDs DISABLED_HungThreadIDs
695 #else
696 #define MAYBE_HungThreadIDs HungThreadIDs
697 #endif
698
TEST_F(HangWatcherSnapshotTest,MAYBE_HungThreadIDs)699 TEST_F(HangWatcherSnapshotTest, MAYBE_HungThreadIDs) {
700 // During hang capture the list of hung threads should be populated.
701 hang_watcher_.SetOnHangClosureForTesting(base::BindLambdaForTesting([this]() {
702 EXPECT_EQ(hang_watcher_.GrabWatchStateSnapshotForTesting()
703 .PrepareHungThreadListCrashKey(),
704 list_of_hung_thread_ids_during_capture_);
705 ++hang_capture_count_;
706 }));
707
708 // When hang capture is over the list should be empty.
709 hang_watcher_.SetAfterMonitorClosureForTesting(
710 base::BindLambdaForTesting([this]() {
711 monitor_event_.Signal();
712 }));
713
714 hang_watcher_.Start();
715
716 // Register the main test thread for hang watching.
717 auto unregister_thread_closure =
718 HangWatcher::RegisterThread(base::HangWatcher::ThreadType::kMainThread);
719
720 BlockingThread blocking_thread(&monitor_event_, base::TimeDelta{});
721 blocking_thread.StartAndWaitForScopeEntered();
722 {
723 // Ensure the blocking thread entered the scope before the main thread. This
724 // will guarantee an ordering while reporting the list of hung threads.
725 task_environment_.AdvanceClock(kSmallCPUQuantum);
726
727 // Start a WatchHangsInScope that expires right away. Ensures that
728 // the first monitor will detect a hang. This scope will naturally have a
729 // later deadline than the one in |blocking_thread_| since it was created
730 // after.
731 WatchHangsInScope expires_instantly(base::TimeDelta{});
732
733 // Hung thread list should contain the id the blocking thread and then the
734 // id of the test main thread since that is the order of increasing
735 // deadline.
736 TestIDList(
737 ConcatenateThreadIds({blocking_thread.GetId(), test_thread_id_}));
738
739 // |expires_instantly| and the scope from |blocking_thread| are still live
740 // but already recorded so should be ignored.
741 ExpectNoCapture();
742
743 // Thread is joinable since we signaled |monitor_event_|. This closes the
744 // scope in |blocking_thread|.
745 blocking_thread.Join();
746
747 // |expires_instantly| is still live but already recorded so should be
748 // ignored.
749 ExpectNoCapture();
750 }
751
752 // All HangWatchScopeEnables are over. There should be no capture.
753 ExpectNoCapture();
754
755 // Once all recorded scopes are over creating a new one and monitoring will
756 // trigger a hang detection.
757 WatchHangsInScope expires_instantly(base::TimeDelta{});
758 TestIDList(ConcatenateThreadIds({test_thread_id_}));
759 }
760
TEST_F(HangWatcherSnapshotTest,TimeSinceLastSystemPowerResumeCrashKey)761 TEST_F(HangWatcherSnapshotTest, TimeSinceLastSystemPowerResumeCrashKey) {
762 // Override the capture of hangs. Simulate a crash key capture.
763 hang_watcher_.SetOnHangClosureForTesting(base::BindLambdaForTesting([this]() {
764 ++hang_capture_count_;
765 seconds_since_last_power_resume_crash_key_ =
766 hang_watcher_.GetTimeSinceLastSystemPowerResumeCrashKeyValue();
767 }));
768
769 // When hang capture is over, unblock the main thread.
770 hang_watcher_.SetAfterMonitorClosureForTesting(
771 base::BindLambdaForTesting([this]() { monitor_event_.Signal(); }));
772
773 hang_watcher_.Start();
774
775 // Register the main test thread for hang watching.
776 auto unregister_thread_closure =
777 HangWatcher::RegisterThread(base::HangWatcher::ThreadType::kMainThread);
778
779 {
780 WatchHangsInScope expires_instantly(base::TimeDelta{});
781 task_environment_.AdvanceClock(kSmallCPUQuantum);
782
783 TriggerMonitorAndWaitForCompletion();
784 EXPECT_EQ(1, hang_capture_count_);
785 EXPECT_EQ("Never suspended", seconds_since_last_power_resume_crash_key_);
786 }
787
788 {
789 test::ScopedPowerMonitorTestSource power_monitor_source;
790 power_monitor_source.Suspend();
791 task_environment_.AdvanceClock(kSmallCPUQuantum);
792
793 {
794 WatchHangsInScope expires_instantly(base::TimeDelta{});
795 task_environment_.AdvanceClock(kSmallCPUQuantum);
796 TriggerMonitorAndWaitForCompletion();
797 EXPECT_EQ(2, hang_capture_count_);
798 EXPECT_EQ("Power suspended", seconds_since_last_power_resume_crash_key_);
799 }
800
801 power_monitor_source.Resume();
802 constexpr TimeDelta kAfterResumeTime{base::Seconds(5)};
803 task_environment_.AdvanceClock(kAfterResumeTime);
804
805 {
806 WatchHangsInScope expires_instantly(base::TimeDelta{});
807 TriggerMonitorAndWaitForCompletion();
808 EXPECT_EQ(3, hang_capture_count_);
809 EXPECT_EQ(base::NumberToString(kAfterResumeTime.InSeconds()),
810 seconds_since_last_power_resume_crash_key_);
811 }
812 }
813 }
814
815 namespace {
816
817 // Determines how long the HangWatcher will wait between calls to
818 // Monitor(). Choose a low value so that that successive invocations happens
819 // fast. This makes tests that wait for monitoring run fast and makes tests that
820 // expect no monitoring fail fast.
821 const base::TimeDelta kMonitoringPeriod = base::Milliseconds(1);
822
823 // Test if and how often the HangWatcher periodically monitors for hangs.
824 class HangWatcherPeriodicMonitoringTest : public testing::Test {
825 public:
HangWatcherPeriodicMonitoringTest()826 HangWatcherPeriodicMonitoringTest() {
827 hang_watcher_.InitializeOnMainThread(
828 HangWatcher::ProcessType::kBrowserProcess, false);
829
830 hang_watcher_.SetMonitoringPeriodForTesting(kMonitoringPeriod);
831 hang_watcher_.SetOnHangClosureForTesting(base::BindRepeating(
832 &WaitableEvent::Signal, base::Unretained(&hang_event_)));
833
834 // HangWatcher uses a TickClock to detect how long it slept in between calls
835 // to Monitor(). Override that clock to control its subjective passage of
836 // time.
837 hang_watcher_.SetTickClockForTesting(&test_clock_);
838 }
839
840 HangWatcherPeriodicMonitoringTest(
841 const HangWatcherPeriodicMonitoringTest& other) = delete;
842 HangWatcherPeriodicMonitoringTest& operator=(
843 const HangWatcherPeriodicMonitoringTest& other) = delete;
844
TearDown()845 void TearDown() override { hang_watcher_.UnitializeOnMainThreadForTesting(); }
846
847 protected:
848 // Setup the callback invoked after waiting in HangWatcher to advance the
849 // tick clock by the desired time delta.
InstallAfterWaitCallback(base::TimeDelta time_delta)850 void InstallAfterWaitCallback(base::TimeDelta time_delta) {
851 hang_watcher_.SetAfterWaitCallbackForTesting(base::BindLambdaForTesting(
852 [this, time_delta](base::TimeTicks time_before_wait) {
853 test_clock_.Advance(time_delta);
854 }));
855 }
856
857 base::SimpleTestTickClock test_clock_;
858
859 // Single threaded to avoid ThreadPool WorkerThreads registering. Will run
860 // delayed tasks created by the tests.
861 test::SingleThreadTaskEnvironment task_environment_;
862
863 std::unique_ptr<base::TickClock> fake_tick_clock_;
864 HangWatcher hang_watcher_;
865
866 // Signaled when a hang is detected.
867 WaitableEvent hang_event_;
868
869 base::ScopedClosureRunner unregister_thread_closure_;
870 };
871 } // namespace
872
873 // Don't register any threads for hang watching. HangWatcher should not monitor.
TEST_F(HangWatcherPeriodicMonitoringTest,NoPeriodicMonitoringWithoutRegisteredThreads)874 TEST_F(HangWatcherPeriodicMonitoringTest,
875 NoPeriodicMonitoringWithoutRegisteredThreads) {
876 RunLoop run_loop;
877
878 // If a call to HangWatcher::Monitor() takes place the test will instantly
879 // fail.
880 hang_watcher_.SetAfterMonitorClosureForTesting(
881 base::BindLambdaForTesting([&run_loop]() {
882 ADD_FAILURE() << "Monitoring took place!";
883 run_loop.Quit();
884 }));
885
886 // Make the HangWatcher tick clock advance by exactly the monitoring period
887 // after waiting so it will never detect oversleeping between attempts to call
888 // Monitor(). This would inhibit monitoring and make the test pass for the
889 // wrong reasons.
890 InstallAfterWaitCallback(kMonitoringPeriod);
891
892 hang_watcher_.Start();
893
894 // Unblock the test thread. No thread ever registered after the HangWatcher
895 // was created in the test's constructor. No monitoring should have taken
896 // place.
897 task_environment_.GetMainThreadTaskRunner()->PostDelayedTask(
898 FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
899 run_loop.Run();
900
901 // NOTE:
902 // A lack of calls could technically also be caused by the HangWatcher thread
903 // executing too slowly / being descheduled. This is a known limitation.
904 // It's expected for |TestTimeouts::tiny_timeout()| to be large enough that
905 // this is rare.
906 }
907
908 // During normal execution periodic monitorings should take place.
TEST_F(HangWatcherPeriodicMonitoringTest,PeriodicCallsTakePlace)909 TEST_F(HangWatcherPeriodicMonitoringTest, PeriodicCallsTakePlace) {
910 // HangWatcher::Monitor() will run once right away on thread registration.
911 // We want to make sure it runs at a couple more times from being scheduled.
912 constexpr int kMinimumMonitorCount = 3;
913
914 RunLoop run_loop;
915
916 // Setup the HangWatcher to unblock run_loop when the Monitor() has been
917 // invoked enough times.
918 hang_watcher_.SetAfterMonitorClosureForTesting(BarrierClosure(
919 kMinimumMonitorCount, base::BindLambdaForTesting([&run_loop]() {
920 // Test condition are confirmed, stop monitoring.
921 HangWatcher::StopMonitoringForTesting();
922
923 // Unblock the test main thread.
924 run_loop.Quit();
925 })));
926
927 // Make the HangWatcher tick clock advance by exactly the monitoring period
928 // after waiting so it will never detect oversleeping between attempts to call
929 // Monitor(). This would inhibit monitoring.
930 InstallAfterWaitCallback(kMonitoringPeriod);
931
932 hang_watcher_.Start();
933
934 // Register a thread,
935 unregister_thread_closure_ =
936 HangWatcher::RegisterThread(base::HangWatcher::ThreadType::kMainThread);
937
938 run_loop.Run();
939
940 // No monitored scope means no possible hangs.
941 ASSERT_FALSE(hang_event_.IsSignaled());
942 }
943
944 // If the HangWatcher detects it slept for longer than expected it will not
945 // monitor.
TEST_F(HangWatcherPeriodicMonitoringTest,NoMonitorOnOverSleep)946 TEST_F(HangWatcherPeriodicMonitoringTest, NoMonitorOnOverSleep) {
947 RunLoop run_loop;
948
949 // If a call to HangWatcher::Monitor() takes place the test will instantly
950 // fail.
951 hang_watcher_.SetAfterMonitorClosureForTesting(
952 base::BindLambdaForTesting([&run_loop]() {
953 ADD_FAILURE() << "Monitoring took place!";
954 run_loop.Quit();
955 }));
956
957 // Make the HangWatcher tick clock advance so much after waiting that it will
958 // detect oversleeping every time. This will keep it from monitoring.
959 InstallAfterWaitCallback(base::Minutes(1));
960
961 hang_watcher_.Start();
962
963 // Register a thread.
964 unregister_thread_closure_ =
965 HangWatcher::RegisterThread(base::HangWatcher::ThreadType::kMainThread);
966
967 // Unblock the test thread. All waits were perceived as oversleeping so all
968 // monitoring was inhibited.
969 task_environment_.GetMainThreadTaskRunner()->PostDelayedTask(
970 FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
971 run_loop.Run();
972
973 // NOTE: A lack of calls could technically also be caused by the HangWatcher
974 // thread executing too slowly / being descheduled. This is a known
975 // limitation. It's expected for |TestTimeouts::tiny_timeout()| to be large
976 // enough that this happens rarely.
977 }
978
979 namespace {
980 class WatchHangsInScopeBlockingTest : public testing::Test {
981 public:
WatchHangsInScopeBlockingTest()982 WatchHangsInScopeBlockingTest() {
983 feature_list_.InitWithFeaturesAndParameters(kFeatureAndParams, {});
984 HangWatcher::InitializeOnMainThread(
985 HangWatcher::ProcessType::kBrowserProcess, false);
986
987 hang_watcher_.SetOnHangClosureForTesting(base::BindLambdaForTesting([&] {
988 capture_started_.Signal();
989 // Simulate capturing that takes a long time.
990 PlatformThread::Sleep(base::Milliseconds(500));
991
992 continue_capture_.Wait();
993 completed_capture_ = true;
994 }));
995
996 hang_watcher_.SetAfterMonitorClosureForTesting(
997 base::BindLambdaForTesting([&] {
998 // Simulate monitoring that takes a long time.
999 PlatformThread::Sleep(base::Milliseconds(500));
1000 completed_monitoring_.Signal();
1001 }));
1002
1003 // Make sure no periodic monitoring takes place.
1004 hang_watcher_.SetMonitoringPeriodForTesting(kVeryLongDelta);
1005
1006 hang_watcher_.Start();
1007
1008 // Register the test main thread for hang watching.
1009 unregister_thread_closure_ =
1010 HangWatcher::RegisterThread(base::HangWatcher::ThreadType::kMainThread);
1011 }
1012
TearDown()1013 void TearDown() override { HangWatcher::UnitializeOnMainThreadForTesting(); }
1014
1015 WatchHangsInScopeBlockingTest(const WatchHangsInScopeBlockingTest& other) =
1016 delete;
1017 WatchHangsInScopeBlockingTest& operator=(
1018 const WatchHangsInScopeBlockingTest& other) = delete;
1019
VerifyScopesDontBlock()1020 void VerifyScopesDontBlock() {
1021 // Start a WatchHangsInScope that cannot possibly cause a hang to be
1022 // detected.
1023 {
1024 WatchHangsInScope long_scope(kVeryLongDelta);
1025
1026 // Manually trigger a monitoring.
1027 hang_watcher_.SignalMonitorEventForTesting();
1028
1029 // Execution has to continue freely here as no capture is in progress.
1030 }
1031
1032 // Monitoring should not be over yet because the test code should execute
1033 // faster when not blocked.
1034 EXPECT_FALSE(completed_monitoring_.IsSignaled());
1035
1036 // Wait for the full monitoring process to be complete. This is to prove
1037 // that monitoring truly executed and that we raced the signaling.
1038 completed_monitoring_.Wait();
1039
1040 // No hang means no capture.
1041 EXPECT_FALSE(completed_capture_);
1042 }
1043
1044 protected:
1045 base::WaitableEvent capture_started_;
1046 base::WaitableEvent completed_monitoring_;
1047
1048 // The HangWatcher waits on this event via the "on hang" closure when a hang
1049 // is detected.
1050 base::WaitableEvent continue_capture_;
1051 bool completed_capture_{false};
1052
1053 base::test::ScopedFeatureList feature_list_;
1054 HangWatcher hang_watcher_;
1055 base::ScopedClosureRunner unregister_thread_closure_;
1056 };
1057 } // namespace
1058
1059 // Tests that execution is unimpeded by ~WatchHangsInScope() when no capture
1060 // ever takes place.
TEST_F(WatchHangsInScopeBlockingTest,ScopeDoesNotBlocksWithoutCapture)1061 TEST_F(WatchHangsInScopeBlockingTest, ScopeDoesNotBlocksWithoutCapture) {
1062 // No capture should take place so |continue_capture_| is not signaled to
1063 // create a test hang if one ever does.
1064 VerifyScopesDontBlock();
1065 }
1066
1067 // Test that execution blocks in ~WatchHangsInScope() for a thread under
1068 // watch during the capturing of a hang.
TEST_F(WatchHangsInScopeBlockingTest,ScopeBlocksDuringCapture)1069 TEST_F(WatchHangsInScopeBlockingTest, ScopeBlocksDuringCapture) {
1070 // The capture completing is not dependent on any test event. Signal to make
1071 // sure the test is not blocked.
1072 continue_capture_.Signal();
1073
1074 // Start a WatchHangsInScope that expires in the past already. Ensures
1075 // that the first monitor will detect a hang.
1076 {
1077 // Start a WatchHangsInScope that expires right away. Ensures that the
1078 // first monitor will detect a hang.
1079 WatchHangsInScope expires_right_away(base::TimeDelta{});
1080
1081 // Manually trigger a monitoring.
1082 hang_watcher_.SignalMonitorEventForTesting();
1083
1084 // Ensure that the hang capturing started.
1085 capture_started_.Wait();
1086
1087 // Execution will get stuck in the outer scope because it can't escape
1088 // ~WatchHangsInScope() if a hang capture is under way.
1089 }
1090
1091 // A hang was in progress so execution should have been blocked in
1092 // BlockWhileCaptureInProgress() until capture finishes.
1093 EXPECT_TRUE(completed_capture_);
1094 completed_monitoring_.Wait();
1095
1096 // Reset expectations
1097 completed_monitoring_.Reset();
1098 capture_started_.Reset();
1099 completed_capture_ = false;
1100
1101 // Verify that scopes don't block just because a capture happened in the past.
1102 VerifyScopesDontBlock();
1103 }
1104
1105 #if BUILDFLAG(IS_MAC) && defined(ARCH_CPU_ARM64)
1106 // Flaky hangs on arm64 Macs: https://crbug.com/1140207
1107 #define MAYBE_NewScopeDoesNotBlockDuringCapture \
1108 DISABLED_NewScopeDoesNotBlockDuringCapture
1109 #else
1110 #define MAYBE_NewScopeDoesNotBlockDuringCapture \
1111 NewScopeDoesNotBlockDuringCapture
1112 #endif
1113
1114 // Test that execution does not block in ~WatchHangsInScope() when the scope
1115 // was created after the start of a capture.
TEST_F(WatchHangsInScopeBlockingTest,MAYBE_NewScopeDoesNotBlockDuringCapture)1116 TEST_F(WatchHangsInScopeBlockingTest, MAYBE_NewScopeDoesNotBlockDuringCapture) {
1117 // Start a WatchHangsInScope that expires right away. Ensures that the
1118 // first monitor will detect a hang.
1119 WatchHangsInScope expires_right_away(base::TimeDelta{});
1120
1121 // Manually trigger a monitoring.
1122 hang_watcher_.SignalMonitorEventForTesting();
1123
1124 // Ensure that the hang capturing started.
1125 capture_started_.Wait();
1126
1127 // A scope started once a capture is already under way should not block
1128 // execution.
1129 { WatchHangsInScope also_expires_right_away(base::TimeDelta{}); }
1130
1131 // Wait for the new WatchHangsInScope to be destroyed to let the capture
1132 // finish. If the new scope block waiting for the capture to finish this would
1133 // create a deadlock and the test would hang.
1134 continue_capture_.Signal();
1135 }
1136
1137 namespace internal {
1138 namespace {
1139
1140 constexpr std::array<HangWatchDeadline::Flag, 3> kAllFlags{
1141 {HangWatchDeadline::Flag::kMinValue,
1142 HangWatchDeadline::Flag::kIgnoreCurrentWatchHangsInScope,
1143 HangWatchDeadline::Flag::kShouldBlockOnHang}};
1144 } // namespace
1145
1146 class HangWatchDeadlineTest : public testing::Test {
1147 protected:
AssertNoFlagsSet() const1148 void AssertNoFlagsSet() const {
1149 for (HangWatchDeadline::Flag flag : kAllFlags) {
1150 ASSERT_FALSE(deadline_.IsFlagSet(flag));
1151 }
1152 }
1153
1154 // Return a flag mask without one of the flags for test purposes. Use to
1155 // ignore that effect of setting a flag that was just set.
FlagsMinus(uint64_t flags,HangWatchDeadline::Flag flag)1156 uint64_t FlagsMinus(uint64_t flags, HangWatchDeadline::Flag flag) {
1157 return flags & ~(static_cast<uint64_t>(flag));
1158 }
1159
1160 HangWatchDeadline deadline_;
1161 };
1162
1163 // Verify that the extract functions don't mangle any bits.
TEST_F(HangWatchDeadlineTest,BitsPreservedThroughExtract)1164 TEST_F(HangWatchDeadlineTest, BitsPreservedThroughExtract) {
1165 for (auto bits : {kAllOnes, kAllZeros, kOnesThenZeroes, kZeroesThenOnes}) {
1166 ASSERT_TRUE((HangWatchDeadline::ExtractFlags(bits) |
1167 HangWatchDeadline::ExtractDeadline(bits)) == bits);
1168 }
1169 }
1170
1171 // Verify that setting and clearing a persistent flag works and has no unwanted
1172 // side-effects. Neither the flags nor the deadline change concurrently in this
1173 // test.
TEST_F(HangWatchDeadlineTest,SetAndClearPersistentFlag)1174 TEST_F(HangWatchDeadlineTest, SetAndClearPersistentFlag) {
1175 AssertNoFlagsSet();
1176
1177 // Grab the original values for flags and deadline.
1178 auto [old_flags, old_deadline] = deadline_.GetFlagsAndDeadline();
1179
1180 // Set the flag. Operation cannot fail.
1181 deadline_.SetIgnoreCurrentWatchHangsInScope();
1182
1183 // Get new flags and deadline.
1184 auto [new_flags, new_deadline] = deadline_.GetFlagsAndDeadline();
1185
1186 // Flag was set properly.
1187 ASSERT_TRUE(HangWatchDeadline::IsFlagSet(
1188 HangWatchDeadline::Flag::kIgnoreCurrentWatchHangsInScope, new_flags));
1189
1190 // No side-effect on deadline.
1191 ASSERT_EQ(new_deadline, old_deadline);
1192
1193 // No side-effect on other flags.
1194 ASSERT_EQ(
1195 FlagsMinus(new_flags,
1196 HangWatchDeadline::Flag::kIgnoreCurrentWatchHangsInScope),
1197 old_flags);
1198
1199 // Clear the flag, operation cannot fail.
1200 deadline_.UnsetIgnoreCurrentWatchHangsInScope();
1201
1202 // Update new values.
1203 std::tie(new_flags, new_deadline) = deadline_.GetFlagsAndDeadline();
1204
1205 // All flags back to original state.
1206 ASSERT_EQ(new_flags, old_flags);
1207
1208 // Deadline still unnafected.
1209 ASSERT_EQ(new_deadline, old_deadline);
1210 }
1211
1212 // Verify setting the TimeTicks value works and has no unwanted side-effects.
TEST_F(HangWatchDeadlineTest,SetDeadline)1213 TEST_F(HangWatchDeadlineTest, SetDeadline) {
1214 TimeTicks ticks;
1215
1216 AssertNoFlagsSet();
1217 ASSERT_NE(deadline_.GetDeadline(), ticks);
1218
1219 // Set the deadline and verify it stuck.
1220 deadline_.SetDeadline(ticks);
1221 ASSERT_EQ(deadline_.GetDeadline(), ticks);
1222
1223 // Only the value was modified, no flags should be set.
1224 AssertNoFlagsSet();
1225 }
1226
1227 // Verify that setting a non-persistent flag (kShouldBlockOnHang)
1228 // when the TimeTicks value changed since calling the flag setting
1229 // function fails and has no side-effects.
TEST_F(HangWatchDeadlineTest,SetShouldBlockOnHangDeadlineChanged)1230 TEST_F(HangWatchDeadlineTest, SetShouldBlockOnHangDeadlineChanged) {
1231 AssertNoFlagsSet();
1232
1233 auto [flags, deadline] = deadline_.GetFlagsAndDeadline();
1234
1235 // Simulate value change. Flags are constant.
1236 const base::TimeTicks new_deadline =
1237 base::TimeTicks::FromInternalValue(kArbitraryDeadline);
1238 ASSERT_NE(deadline, new_deadline);
1239 deadline_.SetSwitchBitsClosureForTesting(
1240 base::BindLambdaForTesting([]() { return kArbitraryDeadline; }));
1241
1242 // kShouldBlockOnHangs does not persist through value change.
1243 ASSERT_FALSE(deadline_.SetShouldBlockOnHang(flags, deadline));
1244
1245 // Flag was not applied.
1246 ASSERT_FALSE(
1247 deadline_.IsFlagSet(HangWatchDeadline::Flag::kShouldBlockOnHang));
1248
1249 // New value that was changed concurrently is preserved.
1250 ASSERT_EQ(deadline_.GetDeadline(), new_deadline);
1251 }
1252
1253 // Verify that clearing a persistent (kIgnoreCurrentWatchHangsInScope) when
1254 // the value changed succeeds and has non side-effects.
TEST_F(HangWatchDeadlineTest,ClearIgnoreHangsDeadlineChanged)1255 TEST_F(HangWatchDeadlineTest, ClearIgnoreHangsDeadlineChanged) {
1256 AssertNoFlagsSet();
1257
1258 auto [flags, deadline] = deadline_.GetFlagsAndDeadline();
1259
1260 deadline_.SetIgnoreCurrentWatchHangsInScope();
1261 std::tie(flags, deadline) = deadline_.GetFlagsAndDeadline();
1262 ASSERT_TRUE(HangWatchDeadline::IsFlagSet(
1263 HangWatchDeadline::Flag::kIgnoreCurrentWatchHangsInScope, flags));
1264
1265 // Simulate deadline change. Flags are constant.
1266 const base::TimeTicks new_deadline =
1267 base::TimeTicks::FromInternalValue(kArbitraryDeadline);
1268 ASSERT_NE(deadline, new_deadline);
1269 deadline_.SetSwitchBitsClosureForTesting(base::BindLambdaForTesting([]() {
1270 return static_cast<uint64_t>(HangWatchDeadline::Flag::kShouldBlockOnHang) |
1271 kArbitraryDeadline;
1272 }));
1273
1274 // Clearing kIgnoreHang is unaffected by deadline or flags change.
1275 deadline_.UnsetIgnoreCurrentWatchHangsInScope();
1276 ASSERT_FALSE(deadline_.IsFlagSet(
1277 HangWatchDeadline::Flag::kIgnoreCurrentWatchHangsInScope));
1278
1279 // New deadline that was changed concurrently is preserved.
1280 ASSERT_TRUE(deadline_.IsFlagSet(HangWatchDeadline::Flag::kShouldBlockOnHang));
1281 ASSERT_EQ(deadline_.GetDeadline(), new_deadline);
1282 }
1283
1284 // Verify that setting a persistent (kIgnoreCurrentWatchHangsInScope) when
1285 // the deadline or flags changed succeeds and has non side-effects.
TEST_F(HangWatchDeadlineTest,SetIgnoreCurrentHangWatchScopeEnableDeadlineChangedd)1286 TEST_F(HangWatchDeadlineTest,
1287 SetIgnoreCurrentHangWatchScopeEnableDeadlineChangedd) {
1288 AssertNoFlagsSet();
1289
1290 auto [flags, deadline] = deadline_.GetFlagsAndDeadline();
1291
1292 // Simulate deadline change. Flags are constant.
1293 const base::TimeTicks new_deadline =
1294 base::TimeTicks::FromInternalValue(kArbitraryDeadline);
1295
1296 ASSERT_NE(deadline, new_deadline);
1297 deadline_.SetSwitchBitsClosureForTesting(base::BindLambdaForTesting([]() {
1298 return static_cast<uint64_t>(HangWatchDeadline::Flag::kShouldBlockOnHang) |
1299 kArbitraryDeadline;
1300 }));
1301
1302 // kIgnoreHang persists through value change.
1303 deadline_.SetIgnoreCurrentWatchHangsInScope();
1304 ASSERT_TRUE(deadline_.IsFlagSet(
1305 HangWatchDeadline::Flag::kIgnoreCurrentWatchHangsInScope));
1306
1307 // New deadline and flags that changed concurrently are preserved.
1308 ASSERT_TRUE(deadline_.IsFlagSet(HangWatchDeadline::Flag::kShouldBlockOnHang));
1309 ASSERT_EQ(deadline_.GetDeadline(), new_deadline);
1310 }
1311
1312 // Setting a new deadline should wipe flags that a not persistent.
1313 // Persistent flags should not be disturbed.
TEST_F(HangWatchDeadlineTest,SetDeadlineWipesFlags)1314 TEST_F(HangWatchDeadlineTest, SetDeadlineWipesFlags) {
1315 auto [flags, deadline] = deadline_.GetFlagsAndDeadline();
1316
1317 ASSERT_TRUE(deadline_.SetShouldBlockOnHang(flags, deadline));
1318 ASSERT_TRUE(deadline_.IsFlagSet(HangWatchDeadline::Flag::kShouldBlockOnHang));
1319
1320 std::tie(flags, deadline) = deadline_.GetFlagsAndDeadline();
1321
1322 deadline_.SetIgnoreCurrentWatchHangsInScope();
1323 ASSERT_TRUE(deadline_.IsFlagSet(
1324 HangWatchDeadline::Flag::kIgnoreCurrentWatchHangsInScope));
1325
1326 // Change the deadline.
1327 deadline_.SetDeadline(TimeTicks{});
1328 ASSERT_EQ(deadline_.GetDeadline(), TimeTicks{});
1329
1330 // Verify the persistent flag stuck and the non-persistent one was unset.
1331 ASSERT_FALSE(
1332 deadline_.IsFlagSet(HangWatchDeadline::Flag::kShouldBlockOnHang));
1333 ASSERT_TRUE(deadline_.IsFlagSet(
1334 HangWatchDeadline::Flag::kIgnoreCurrentWatchHangsInScope));
1335 }
1336
1337 } // namespace internal
1338
1339 } // namespace base
1340