chromium/base/threading/hang_watcher_unittest.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/threading/hang_watcher.h"

#include <atomic>
#include <memory>
#include <optional>

#include "base/barrier_closure.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/field_trial_params.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/power_monitor_test.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/test/task_environment.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_checker.h"
#include "base/threading/threading_features.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

ElementsAre;
IsEmpty;

namespace base {
namespace {

// Use with a FeatureList to activate crash dumping for threads marked as
// threadpool threads.
const std::vector<base::test::FeatureRefAndParams> kFeatureAndParams{};

// Use this value to mark things very far off in the future. Adding this
// to TimeTicks::Now() gives a point that will never be reached during the
// normal execution of a test.
constexpr TimeDelta kVeryLongDelta{};

// A relatively small time delta to ensure ordering of hung threads list.
constexpr TimeDelta kSmallCPUQuantum{};

constexpr uint64_t kArbitraryDeadline =;
constexpr uint64_t kAllOnes =;
constexpr uint64_t kAllZeros =;
constexpr uint64_t kOnesThenZeroes =;
constexpr uint64_t kZeroesThenOnes =;

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
class HangWatcherEnabledInZygoteChildTest
    : public testing::TestWithParam<std::tuple<bool, bool>> {};

TEST_P(HangWatcherEnabledInZygoteChildTest, IsEnabled) {}

INSTANTIATE_TEST_SUITE_P();
#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)

// Waits on provided WaitableEvent before executing and signals when done.
class BlockingThread : public DelegateSimpleThread::Delegate {};

class HangWatcherTest : public testing::Test {};

class HangWatcherBlockingThreadTest : public HangWatcherTest {};
}  // namespace

TEST_F(HangWatcherTest, InvalidatingExpectationsPreventsCapture) {}

TEST_F(HangWatcherTest, MultipleInvalidateExpectationsDoNotCancelOut) {}

TEST_F(HangWatcherTest, NewInnerWatchHangsInScopeAfterInvalidationDetectsHang) {}

TEST_F(HangWatcherTest,
       NewSeparateWatchHangsInScopeAfterInvalidationDetectsHang) {}

// Test that invalidating expectations from inner WatchHangsInScope will also
// prevent hang detection in outer scopes.
TEST_F(HangWatcherTest, ScopeDisabledObjectInnerScope) {}

TEST_F(HangWatcherTest, NewScopeAfterDisabling) {}

TEST_F(HangWatcherTest, NestedScopes) {}

TEST_F(HangWatcherBlockingThreadTest, HistogramsLoggedOnHang) {}

TEST_F(HangWatcherBlockingThreadTest, HistogramsLoggedWithoutHangs) {}

TEST_F(HangWatcherBlockingThreadTest, HistogramsLoggedWithShutdownFlag) {}

TEST_F(HangWatcherBlockingThreadTest, Hang) {}

TEST_F(HangWatcherBlockingThreadTest, HangAlreadyRecorded) {}

TEST_F(HangWatcherBlockingThreadTest, NoHang) {}

namespace {
class HangWatcherSnapshotTest : public testing::Test {};
}  // namespace

// Verify that the hang capture fails when marking a thread for blocking fails.
// This simulates a WatchHangsInScope completing between the time the hang
// was dected and the time it is recorded which would create a non-actionable
// report.
TEST_F(HangWatcherSnapshotTest, NonActionableReport) {}

// TODO(crbug.com/40187449): On MAC, the base::PlatformThread::CurrentId(...)
// should return the system wide IDs. The HungThreadIDs test fails because the
// reported process ids do not match.
#if BUILDFLAG(IS_MAC)
#define MAYBE_HungThreadIDs
#else
#define MAYBE_HungThreadIDs
#endif

TEST_F(HangWatcherSnapshotTest, MAYBE_HungThreadIDs) {}

TEST_F(HangWatcherSnapshotTest, TimeSinceLastSystemPowerResumeCrashKey) {}

namespace {

// Determines how long the HangWatcher will wait between calls to
// Monitor(). Choose a low value so that that successive invocations happens
// fast. This makes tests that wait for monitoring run fast and makes tests that
// expect no monitoring fail fast.
const base::TimeDelta kMonitoringPeriod =;

// Test if and how often the HangWatcher periodically monitors for hangs.
class HangWatcherPeriodicMonitoringTest : public testing::Test {};
}  // namespace

// Don't register any threads for hang watching. HangWatcher should not monitor.
TEST_F(HangWatcherPeriodicMonitoringTest,
       NoPeriodicMonitoringWithoutRegisteredThreads) {}

// During normal execution periodic monitorings should take place.
TEST_F(HangWatcherPeriodicMonitoringTest, PeriodicCallsTakePlace) {}

// If the HangWatcher detects it slept for longer than expected it will not
// monitor.
TEST_F(HangWatcherPeriodicMonitoringTest, NoMonitorOnOverSleep) {}

namespace {
class WatchHangsInScopeBlockingTest : public testing::Test {};
}  // namespace

// Tests that execution is unimpeded by ~WatchHangsInScope() when no capture
// ever takes place.
TEST_F(WatchHangsInScopeBlockingTest, ScopeDoesNotBlocksWithoutCapture) {}

// Test that execution blocks in ~WatchHangsInScope() for a thread under
// watch during the capturing of a hang.
TEST_F(WatchHangsInScopeBlockingTest, ScopeBlocksDuringCapture) {}

#if BUILDFLAG(IS_MAC) && defined(ARCH_CPU_ARM64)
// Flaky hangs on arm64 Macs: https://crbug.com/1140207
#define MAYBE_NewScopeDoesNotBlockDuringCapture
#else
#define MAYBE_NewScopeDoesNotBlockDuringCapture
#endif

// Test that execution does not block in ~WatchHangsInScope() when the scope
// was created after the start of a capture.
TEST_F(WatchHangsInScopeBlockingTest, MAYBE_NewScopeDoesNotBlockDuringCapture) {}

namespace internal {
namespace {

constexpr std::array<HangWatchDeadline::Flag, 3> kAllFlags{};
}  // namespace

class HangWatchDeadlineTest : public testing::Test {};

// Verify that the extract functions don't mangle any bits.
TEST_F(HangWatchDeadlineTest, BitsPreservedThroughExtract) {}

// Verify that setting and clearing a persistent flag works and has no unwanted
// side-effects. Neither the flags nor the deadline change concurrently in this
// test.
TEST_F(HangWatchDeadlineTest, SetAndClearPersistentFlag) {}

// Verify setting the TimeTicks value works and has no unwanted side-effects.
TEST_F(HangWatchDeadlineTest, SetDeadline) {}

// Verify that setting a non-persistent flag (kShouldBlockOnHang)
// when the TimeTicks value changed since calling the flag setting
// function fails and has no side-effects.
TEST_F(HangWatchDeadlineTest, SetShouldBlockOnHangDeadlineChanged) {}

// Verify that clearing a persistent (kIgnoreCurrentWatchHangsInScope) when
// the value changed succeeds and has non side-effects.
TEST_F(HangWatchDeadlineTest, ClearIgnoreHangsDeadlineChanged) {}

// Verify that setting a persistent (kIgnoreCurrentWatchHangsInScope) when
// the deadline or flags changed succeeds and has non side-effects.
TEST_F(HangWatchDeadlineTest,
       SetIgnoreCurrentHangWatchScopeEnableDeadlineChangedd) {}

// Setting a new deadline should wipe flags that a not persistent.
// Persistent flags should not be disturbed.
TEST_F(HangWatchDeadlineTest, SetDeadlineWipesFlags) {}

}  // namespace internal

}  // namespace base