mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-12-04 19:26:09 +03:00
690 lines
27 KiB
C
690 lines
27 KiB
C
|
// 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.
|
||
|
|
||
|
#ifndef BASE_THREADING_HANG_WATCHER_H_
|
||
|
#define BASE_THREADING_HANG_WATCHER_H_
|
||
|
|
||
|
#include <atomic>
|
||
|
#include <cstdint>
|
||
|
#include <memory>
|
||
|
#include <type_traits>
|
||
|
#include <vector>
|
||
|
|
||
|
#include "base/atomicops.h"
|
||
|
#include "base/auto_reset.h"
|
||
|
#include "base/base_export.h"
|
||
|
#include "base/bits.h"
|
||
|
#include "base/compiler_specific.h"
|
||
|
#include "base/dcheck_is_on.h"
|
||
|
#include "base/debug/crash_logging.h"
|
||
|
#include "base/functional/callback.h"
|
||
|
#include "base/functional/callback_forward.h"
|
||
|
#include "base/functional/callback_helpers.h"
|
||
|
#include "base/gtest_prod_util.h"
|
||
|
#include "base/memory/memory_pressure_listener.h"
|
||
|
#include "base/memory/raw_ptr.h"
|
||
|
#include "base/synchronization/lock.h"
|
||
|
#include "base/synchronization/waitable_event.h"
|
||
|
#include "base/template_util.h"
|
||
|
#include "base/thread_annotations.h"
|
||
|
#include "base/threading/platform_thread.h"
|
||
|
#include "base/threading/simple_thread.h"
|
||
|
#include "base/threading/thread_checker.h"
|
||
|
#include "base/time/tick_clock.h"
|
||
|
#include "base/time/time.h"
|
||
|
#include "build/build_config.h"
|
||
|
|
||
|
namespace base {
|
||
|
class WatchHangsInScope;
|
||
|
namespace internal {
|
||
|
class HangWatchState;
|
||
|
} // namespace internal
|
||
|
} // namespace base
|
||
|
|
||
|
namespace base {
|
||
|
|
||
|
// Instantiate a WatchHangsInScope in a code scope to register to be
|
||
|
// watched for hangs of more than |timeout| by the HangWatcher.
|
||
|
//
|
||
|
// Example usage:
|
||
|
//
|
||
|
// void FooBar(){
|
||
|
// WatchHangsInScope scope(base::Seconds(5));
|
||
|
// DoWork();
|
||
|
// }
|
||
|
//
|
||
|
// If DoWork() takes more than 5s to run and the HangWatcher
|
||
|
// inspects the thread state before Foobar returns a hang will be
|
||
|
// reported.
|
||
|
//
|
||
|
// WatchHangsInScopes are typically meant to live on the stack. In some
|
||
|
// cases it's necessary to keep a WatchHangsInScope instance as a class
|
||
|
// member but special care is required when doing so as a WatchHangsInScope
|
||
|
// that stays alive longer than intended will generate non-actionable hang
|
||
|
// reports.
|
||
|
class BASE_EXPORT [[maybe_unused, nodiscard]] WatchHangsInScope {
|
||
|
public:
|
||
|
// A good default value needs to be large enough to represent a significant
|
||
|
// hang and avoid noise while being small enough to not exclude too many
|
||
|
// hangs. The nature of the work that gets executed on the thread is also
|
||
|
// important. We can be much stricter when monitoring a UI thread compared to
|
||
|
// a ThreadPool thread for example.
|
||
|
static constexpr base::TimeDelta kDefaultHangWatchTime = base::Seconds(10);
|
||
|
|
||
|
// Constructing/destructing thread must be the same thread.
|
||
|
explicit WatchHangsInScope(TimeDelta timeout = kDefaultHangWatchTime);
|
||
|
~WatchHangsInScope();
|
||
|
|
||
|
WatchHangsInScope(const WatchHangsInScope&) = delete;
|
||
|
WatchHangsInScope& operator=(const WatchHangsInScope&) = delete;
|
||
|
|
||
|
private:
|
||
|
// Will be true if the object actually set a deadline and false if not.
|
||
|
bool took_effect_ = true;
|
||
|
|
||
|
// This object should always be constructed and destructed on the same thread.
|
||
|
THREAD_CHECKER(thread_checker_);
|
||
|
|
||
|
// The deadline set by the previous WatchHangsInScope created on this
|
||
|
// thread. Stored so it can be restored when this WatchHangsInScope is
|
||
|
// destroyed.
|
||
|
TimeTicks previous_deadline_;
|
||
|
|
||
|
// Indicates whether the kIgnoreCurrentWatchHangsInScope flag must be set upon
|
||
|
// exiting this WatchHangsInScope if a call to InvalidateActiveExpectations()
|
||
|
// previously suspended hang watching.
|
||
|
bool set_hangs_ignored_on_exit_ = false;
|
||
|
|
||
|
#if DCHECK_IS_ON()
|
||
|
// The previous WatchHangsInScope created on this thread.
|
||
|
raw_ptr<WatchHangsInScope> previous_watch_hangs_in_scope_;
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
// Monitors registered threads for hangs by inspecting their associated
|
||
|
// HangWatchStates for deadline overruns. This happens at a regular interval on
|
||
|
// a separate thread. Only one instance of HangWatcher can exist at a time
|
||
|
// within a single process. This instance must outlive all monitored threads.
|
||
|
class BASE_EXPORT HangWatcher : public DelegateSimpleThread::Delegate {
|
||
|
public:
|
||
|
// Describes the type of a process for logging purposes.
|
||
|
enum class ProcessType {
|
||
|
kUnknownProcess = 0,
|
||
|
kBrowserProcess = 1,
|
||
|
kGPUProcess = 2,
|
||
|
kRendererProcess = 3,
|
||
|
kUtilityProcess = 4,
|
||
|
kMax = kUtilityProcess
|
||
|
};
|
||
|
|
||
|
// Describes the type of a thread for logging purposes.
|
||
|
enum class ThreadType {
|
||
|
kIOThread = 0,
|
||
|
kMainThread = 1,
|
||
|
kThreadPoolThread = 2,
|
||
|
kMax = kThreadPoolThread
|
||
|
};
|
||
|
|
||
|
// Notes on lifetime:
|
||
|
// 1) The first invocation of the constructor will set the global instance
|
||
|
// accessible through GetInstance().
|
||
|
// 2) In production HangWatcher is always purposefuly leaked.
|
||
|
// 3) If not leaked HangWatcher is always constructed and destructed from
|
||
|
// the same thread.
|
||
|
// 4) There can never be more than one instance of HangWatcher at a time.
|
||
|
// The class is not base::Singleton derived because it needs to destroyed
|
||
|
// in tests.
|
||
|
HangWatcher();
|
||
|
|
||
|
// Clears the global instance for the class.
|
||
|
~HangWatcher() override;
|
||
|
|
||
|
HangWatcher(const HangWatcher&) = delete;
|
||
|
HangWatcher& operator=(const HangWatcher&) = delete;
|
||
|
|
||
|
static void CreateHangWatcherInstance();
|
||
|
|
||
|
// Returns a non-owning pointer to the global HangWatcher instance.
|
||
|
static HangWatcher* GetInstance();
|
||
|
|
||
|
// Initializes HangWatcher. Must be called once on the main thread during
|
||
|
// startup while single-threaded.
|
||
|
static void InitializeOnMainThread(ProcessType process_type,
|
||
|
bool is_zygote_child);
|
||
|
|
||
|
// Returns the values that were set through InitializeOnMainThread() to their
|
||
|
// default value. Used for testing since in prod initialization should happen
|
||
|
// only once.
|
||
|
static void UnitializeOnMainThreadForTesting();
|
||
|
|
||
|
// Thread safe functions to verify if hang watching is activated. If called
|
||
|
// before InitializeOnMainThread returns the default value which is false.
|
||
|
static bool IsEnabled();
|
||
|
static bool IsThreadPoolHangWatchingEnabled();
|
||
|
static bool IsIOThreadHangWatchingEnabled();
|
||
|
|
||
|
// Returns true if crash dump reporting is configured for any thread type.
|
||
|
static bool IsCrashReportingEnabled();
|
||
|
|
||
|
// Use to avoid capturing hangs for operations known to take unbounded time
|
||
|
// like waiting for user input. WatchHangsInScope objects created after this
|
||
|
// call will take effect. To resume watching for hangs create a new
|
||
|
// WatchHangsInScope after the unbounded operation finishes.
|
||
|
//
|
||
|
// Example usage:
|
||
|
// {
|
||
|
// WatchHangsInScope scope_1;
|
||
|
// {
|
||
|
// WatchHangsInScope scope_2;
|
||
|
// InvalidateActiveExpectations();
|
||
|
// WaitForUserInput();
|
||
|
// }
|
||
|
//
|
||
|
// WatchHangsInScope scope_4;
|
||
|
// }
|
||
|
//
|
||
|
// WatchHangsInScope scope_5;
|
||
|
//
|
||
|
// In this example hang watching is disabled for WatchHangsInScopes 1 and 2
|
||
|
// since they were both active at the time of the invalidation.
|
||
|
// WatchHangsInScopes 4 and 5 are unaffected since they were created after the
|
||
|
// end of the WatchHangsInScope that was current at the time of invalidation.
|
||
|
//
|
||
|
static void InvalidateActiveExpectations();
|
||
|
|
||
|
// Sets up the calling thread to be monitored for threads. Returns a
|
||
|
// ScopedClosureRunner that unregisters the thread. This closure has to be
|
||
|
// called from the registered thread before it's joined. Returns a null
|
||
|
// closure in the case where there is no HangWatcher instance to register the
|
||
|
// thread with.
|
||
|
[[nodiscard]] static ScopedClosureRunner RegisterThread(
|
||
|
ThreadType thread_type);
|
||
|
|
||
|
// Choose a closure to be run at the end of each call to Monitor(). Use only
|
||
|
// for testing. Reentering the HangWatcher in the closure must be done with
|
||
|
// care. It should only be done through certain testing functions because
|
||
|
// deadlocks are possible.
|
||
|
void SetAfterMonitorClosureForTesting(base::RepeatingClosure closure);
|
||
|
|
||
|
// Choose a closure to be run instead of recording the hang. Used to test
|
||
|
// that certain conditions hold true at the time of recording. Use only
|
||
|
// for testing. Reentering the HangWatcher in the closure must be done with
|
||
|
// care. It should only be done through certain testing functions because
|
||
|
// deadlocks are possible.
|
||
|
void SetOnHangClosureForTesting(base::RepeatingClosure closure);
|
||
|
|
||
|
// Set a monitoring period other than the default. Use only for
|
||
|
// testing.
|
||
|
void SetMonitoringPeriodForTesting(base::TimeDelta period);
|
||
|
|
||
|
// Choose a callback to invoke right after waiting to monitor in Wait(). Use
|
||
|
// only for testing.
|
||
|
void SetAfterWaitCallbackForTesting(
|
||
|
RepeatingCallback<void(TimeTicks)> callback);
|
||
|
|
||
|
// Force the monitoring loop to resume and evaluate whether to continue.
|
||
|
// This can trigger a call to Monitor() or not depending on why the
|
||
|
// HangWatcher thread is sleeping. Use only for testing.
|
||
|
void SignalMonitorEventForTesting();
|
||
|
|
||
|
// Call to make sure no more monitoring takes place. The
|
||
|
// function is thread-safe and can be called at anytime but won't stop
|
||
|
// monitoring that is currently taking place. Use only for testing.
|
||
|
static void StopMonitoringForTesting();
|
||
|
|
||
|
// Replace the clock used when calculating time spent
|
||
|
// sleeping. Use only for testing.
|
||
|
void SetTickClockForTesting(const base::TickClock* tick_clock);
|
||
|
|
||
|
// Use to block until the hang is recorded. Allows the caller to halt
|
||
|
// execution so it does not overshoot the hang watch target and result in a
|
||
|
// non-actionable stack trace in the crash recorded.
|
||
|
void BlockIfCaptureInProgress();
|
||
|
|
||
|
// Begin executing the monitoring loop on the HangWatcher thread.
|
||
|
void Start();
|
||
|
|
||
|
// Returns true if Start() has been called and Stop() has not been called
|
||
|
// since.
|
||
|
bool IsStarted() const { return thread_started_; }
|
||
|
|
||
|
// Returns the value of the crash key with the time since last system power
|
||
|
// resume.
|
||
|
std::string GetTimeSinceLastSystemPowerResumeCrashKeyValue() const;
|
||
|
|
||
|
private:
|
||
|
// See comment of ::RegisterThread() for details.
|
||
|
[[nodiscard]] ScopedClosureRunner RegisterThreadInternal(
|
||
|
ThreadType thread_type) LOCKS_EXCLUDED(watch_state_lock_);
|
||
|
|
||
|
// Use to assert that functions are called on the monitoring thread.
|
||
|
THREAD_CHECKER(hang_watcher_thread_checker_);
|
||
|
|
||
|
// Use to assert that functions are called on the constructing thread.
|
||
|
THREAD_CHECKER(constructing_thread_checker_);
|
||
|
|
||
|
// Invoked on memory pressure signal.
|
||
|
void OnMemoryPressure(
|
||
|
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
|
||
|
|
||
|
#if !BUILDFLAG(IS_NACL)
|
||
|
// Returns a ScopedCrashKeyString that sets the crash key with the time since
|
||
|
// last critical memory pressure signal.
|
||
|
[[nodiscard]] debug::ScopedCrashKeyString
|
||
|
GetTimeSinceLastCriticalMemoryPressureCrashKey();
|
||
|
#endif
|
||
|
|
||
|
// Invoke base::debug::DumpWithoutCrashing() insuring that the stack frame
|
||
|
// right under it in the trace belongs to HangWatcher for easier attribution.
|
||
|
NOINLINE static void RecordHang();
|
||
|
|
||
|
using HangWatchStates =
|
||
|
std::vector<std::unique_ptr<internal::HangWatchState>>;
|
||
|
|
||
|
// Used to save a snapshots of the state of hang watching during capture.
|
||
|
// Only the state of hung threads is retained.
|
||
|
class BASE_EXPORT WatchStateSnapShot {
|
||
|
public:
|
||
|
struct WatchStateCopy {
|
||
|
base::TimeTicks deadline;
|
||
|
base::PlatformThreadId thread_id;
|
||
|
};
|
||
|
|
||
|
WatchStateSnapShot();
|
||
|
WatchStateSnapShot(const WatchStateSnapShot& other);
|
||
|
~WatchStateSnapShot();
|
||
|
|
||
|
// Initialize the snapshot from provided data. |snapshot_time| can be
|
||
|
// different than now() to be coherent with other operations recently done
|
||
|
// on |watch_states|. |hung_watch_state_copies_| can be empty after
|
||
|
// initialization for a number of reasons:
|
||
|
// 1. If any deadline in |watch_states| is before
|
||
|
// |deadline_ignore_threshold|.
|
||
|
// 2. If some of the hung threads could not be marked as blocking on
|
||
|
// capture.
|
||
|
// 3. If none of the hung threads are of a type configured to trigger a
|
||
|
// crash dump.
|
||
|
//
|
||
|
// This function cannot be called more than once without an associated call
|
||
|
// to Clear().
|
||
|
void Init(const HangWatchStates& watch_states,
|
||
|
base::TimeTicks deadline_ignore_threshold);
|
||
|
|
||
|
// Reset the snapshot object to be reused. Can only be called after Init().
|
||
|
void Clear();
|
||
|
|
||
|
// Returns a string that contains the ids of the hung threads separated by a
|
||
|
// '|'. The size of the string is capped at debug::CrashKeySize::Size256. If
|
||
|
// no threads are hung returns an empty string. Can only be invoked if
|
||
|
// IsActionable(). Can only be called after Init().
|
||
|
std::string PrepareHungThreadListCrashKey() const;
|
||
|
|
||
|
// Return the highest deadline included in this snapshot. Can only be called
|
||
|
// if IsActionable(). Can only be called after Init().
|
||
|
base::TimeTicks GetHighestDeadline() const;
|
||
|
|
||
|
// Returns true if the snapshot can be used to record an actionable hang
|
||
|
// report and false if not. Can only be called after Init().
|
||
|
bool IsActionable() const;
|
||
|
|
||
|
private:
|
||
|
bool initialized_ = false;
|
||
|
std::vector<WatchStateCopy> hung_watch_state_copies_;
|
||
|
};
|
||
|
|
||
|
// Return a watch state snapshot taken Now() to be inspected in tests.
|
||
|
// NO_THREAD_SAFETY_ANALYSIS is needed because the analyzer can't figure out
|
||
|
// that calls to this function done from |on_hang_closure_| are properly
|
||
|
// locked.
|
||
|
WatchStateSnapShot GrabWatchStateSnapshotForTesting() const
|
||
|
NO_THREAD_SAFETY_ANALYSIS;
|
||
|
|
||
|
// Inspects the state of all registered threads to check if they are hung and
|
||
|
// invokes the appropriate closure if so.
|
||
|
void Monitor() LOCKS_EXCLUDED(watch_state_lock_);
|
||
|
|
||
|
// Record the hang crash dump and perform the necessary housekeeping before
|
||
|
// and after.
|
||
|
void DoDumpWithoutCrashing(const WatchStateSnapShot& watch_state_snapshot)
|
||
|
EXCLUSIVE_LOCKS_REQUIRED(watch_state_lock_) LOCKS_EXCLUDED(capture_lock_);
|
||
|
|
||
|
// Stop all monitoring and join the HangWatcher thread.
|
||
|
void Stop();
|
||
|
|
||
|
// Wait until it's time to monitor.
|
||
|
void Wait();
|
||
|
|
||
|
// Run the loop that periodically monitors the registered thread at a
|
||
|
// set time interval.
|
||
|
void Run() override;
|
||
|
|
||
|
base::TimeDelta monitor_period_;
|
||
|
|
||
|
// Use to make the HangWatcher thread wake or sleep to schedule the
|
||
|
// appropriate monitoring frequency.
|
||
|
WaitableEvent should_monitor_;
|
||
|
|
||
|
bool IsWatchListEmpty() LOCKS_EXCLUDED(watch_state_lock_);
|
||
|
|
||
|
// Stops hang watching on the calling thread by removing the entry from the
|
||
|
// watch list.
|
||
|
void UnregisterThread() LOCKS_EXCLUDED(watch_state_lock_);
|
||
|
|
||
|
Lock watch_state_lock_;
|
||
|
|
||
|
std::vector<std::unique_ptr<internal::HangWatchState>> watch_states_
|
||
|
GUARDED_BY(watch_state_lock_);
|
||
|
|
||
|
// Snapshot to be reused across hang captures. The point of keeping it
|
||
|
// around is reducing allocations during capture.
|
||
|
WatchStateSnapShot watch_state_snapshot_
|
||
|
GUARDED_BY_CONTEXT(hang_watcher_thread_checker_);
|
||
|
|
||
|
base::DelegateSimpleThread thread_;
|
||
|
bool thread_started_ = false;
|
||
|
|
||
|
RepeatingClosure after_monitor_closure_for_testing_;
|
||
|
RepeatingClosure on_hang_closure_for_testing_;
|
||
|
RepeatingCallback<void(TimeTicks)> after_wait_callback_;
|
||
|
|
||
|
base::Lock capture_lock_ ACQUIRED_AFTER(watch_state_lock_);
|
||
|
std::atomic<bool> capture_in_progress_{false};
|
||
|
|
||
|
raw_ptr<const base::TickClock> tick_clock_;
|
||
|
|
||
|
// Registration to receive memory pressure signals.
|
||
|
base::MemoryPressureListener memory_pressure_listener_;
|
||
|
|
||
|
// The last time at which a critical memory pressure signal was received, or
|
||
|
// null if no signal was ever received. Atomic because it's set and read from
|
||
|
// different threads.
|
||
|
std::atomic<base::TimeTicks> last_critical_memory_pressure_{
|
||
|
base::TimeTicks()};
|
||
|
|
||
|
// The time after which all deadlines in |watch_states_| need to be for a hang
|
||
|
// to be reported.
|
||
|
base::TimeTicks deadline_ignore_threshold_;
|
||
|
|
||
|
FRIEND_TEST_ALL_PREFIXES(HangWatcherTest, NestedScopes);
|
||
|
FRIEND_TEST_ALL_PREFIXES(HangWatcherSnapshotTest, HungThreadIDs);
|
||
|
FRIEND_TEST_ALL_PREFIXES(HangWatcherSnapshotTest, NonActionableReport);
|
||
|
};
|
||
|
|
||
|
// Classes here are exposed in the header only for testing. They are not
|
||
|
// intended to be used outside of base.
|
||
|
namespace internal {
|
||
|
|
||
|
// Threadsafe class that manages a deadline of type TimeTicks alongside hang
|
||
|
// watching specific flags. The flags are stored in the higher bits of the
|
||
|
// underlying TimeTicks deadline. This enables setting the flags on thread T1 in
|
||
|
// a way that's resilient to concurrent deadline or flag changes from thread T2.
|
||
|
// Flags can be queried separately from the deadline and users of this class
|
||
|
// should not have to care about them when doing so.
|
||
|
class BASE_EXPORT HangWatchDeadline {
|
||
|
public:
|
||
|
// Masks to set flags by flipping a single bit in the TimeTicks value. There
|
||
|
// are two types of flags. Persistent flags remain set through a deadline
|
||
|
// change and non-persistent flags are cleared when the deadline changes.
|
||
|
enum class Flag : uint64_t {
|
||
|
// Minimum value for validation purposes. Not currently used.
|
||
|
kMinValue = bits::LeftmostBit<uint64_t>() >> 7,
|
||
|
// Persistent because if hang detection is disabled on a thread it should
|
||
|
// be re-enabled manually.
|
||
|
kIgnoreCurrentWatchHangsInScope = bits::LeftmostBit<uint64_t>() >> 1,
|
||
|
// Non-persistent because a new value means a new WatchHangsInScope started
|
||
|
// after the beginning of capture. It can't be implicated in the hang so we
|
||
|
// don't want it to block.
|
||
|
kShouldBlockOnHang = bits::LeftmostBit<uint64_t>() >> 0,
|
||
|
kMaxValue = kShouldBlockOnHang
|
||
|
};
|
||
|
|
||
|
HangWatchDeadline();
|
||
|
~HangWatchDeadline();
|
||
|
|
||
|
// HangWatchDeadline should never be copied. To keep a copy of the deadline or
|
||
|
// flags use the appropriate accessors.
|
||
|
HangWatchDeadline(const HangWatchDeadline&) = delete;
|
||
|
HangWatchDeadline& operator=(const HangWatchDeadline&) = delete;
|
||
|
|
||
|
// Returns the underlying TimeTicks deadline. WARNING: The deadline and flags
|
||
|
// can change concurrently. To inspect both, use GetFlagsAndDeadline() to get
|
||
|
// a coherent race-free view of the state.
|
||
|
TimeTicks GetDeadline() const;
|
||
|
|
||
|
// Returns a mask containing the flags and the deadline as a pair. Use to
|
||
|
// inspect the flags and deadline and then optionally call
|
||
|
// SetShouldBlockOnHang() .
|
||
|
std::pair<uint64_t, TimeTicks> GetFlagsAndDeadline() const;
|
||
|
|
||
|
// Returns true if the flag is set and false if not. WARNING: The deadline and
|
||
|
// flags can change concurrently. To inspect both, use GetFlagsAndDeadline()
|
||
|
// to get a coherent race-free view of the state.
|
||
|
bool IsFlagSet(Flag flag) const;
|
||
|
|
||
|
// Returns true if a flag is set in |flags| and false if not. Use to inspect
|
||
|
// the flags mask returned by GetFlagsAndDeadline(). WARNING: The deadline and
|
||
|
// flags can change concurrently. If you need to inspect both you need to use
|
||
|
// GetFlagsAndDeadline() to get a coherent race-free view of the state.
|
||
|
static bool IsFlagSet(Flag flag, uint64_t flags);
|
||
|
|
||
|
// Replace the deadline value. |new_value| needs to be within [0,
|
||
|
// Max()]. This function can never fail.
|
||
|
void SetDeadline(TimeTicks new_value);
|
||
|
|
||
|
// Sets the kShouldBlockOnHang flag and returns true if current flags and
|
||
|
// deadline are still equal to |old_flags| and |old_deadline|. Otherwise does
|
||
|
// not set the flag and returns false.
|
||
|
bool SetShouldBlockOnHang(uint64_t old_flags, TimeTicks old_deadline);
|
||
|
|
||
|
// Sets the kIgnoreCurrentWatchHangsInScope flag.
|
||
|
void SetIgnoreCurrentWatchHangsInScope();
|
||
|
|
||
|
// Clears the kIgnoreCurrentWatchHangsInScope flag.
|
||
|
void UnsetIgnoreCurrentWatchHangsInScope();
|
||
|
|
||
|
// Use to simulate the value of |bits_| changing between the calling a
|
||
|
// Set* function and the moment of atomically switching the values. The
|
||
|
// callback should return a value containing the desired flags and deadline
|
||
|
// bits. The flags that are already set will be preserved upon applying. Use
|
||
|
// only for testing.
|
||
|
void SetSwitchBitsClosureForTesting(
|
||
|
RepeatingCallback<uint64_t(void)> closure);
|
||
|
|
||
|
// Remove the deadline modification callback for when testing is done. Use
|
||
|
// only for testing.
|
||
|
void ResetSwitchBitsClosureForTesting();
|
||
|
|
||
|
private:
|
||
|
using TimeTicksInternalRepresentation =
|
||
|
std::invoke_result<decltype(&TimeTicks::ToInternalValue),
|
||
|
TimeTicks>::type;
|
||
|
static_assert(std::is_same_v<TimeTicksInternalRepresentation, int64_t>,
|
||
|
"Bit manipulations made by HangWatchDeadline need to be"
|
||
|
"adapted if internal representation of TimeTicks changes.");
|
||
|
|
||
|
// Replace the bits with the ones provided through the callback. Preserves the
|
||
|
// flags that were already set. Returns the switched in bits. Only call if
|
||
|
// |switch_bits_callback_for_testing_| is installed.
|
||
|
uint64_t SwitchBitsForTesting();
|
||
|
|
||
|
// Atomically sets persitent flag |flag|. Cannot fail.
|
||
|
void SetPersistentFlag(Flag flag);
|
||
|
|
||
|
// Atomically clears persitent flag |flag|. Cannot fail.
|
||
|
void ClearPersistentFlag(Flag flag);
|
||
|
|
||
|
// Converts bits to TimeTicks with some sanity checks. Use to return the
|
||
|
// deadline outside of this class.
|
||
|
static TimeTicks DeadlineFromBits(uint64_t bits);
|
||
|
|
||
|
// Returns the largest representable deadline.
|
||
|
static TimeTicks Max();
|
||
|
|
||
|
// Extract the flag bits from |bits|.
|
||
|
static uint64_t ExtractFlags(uint64_t bits);
|
||
|
|
||
|
// Extract the deadline bits from |bits|.
|
||
|
static uint64_t ExtractDeadline(uint64_t bits);
|
||
|
|
||
|
// BitsType is uint64_t. This type is chosen for having
|
||
|
// std::atomic<BitsType>{}.is_lock_free() true on many platforms and having no
|
||
|
// undefined behaviors with regards to bit shift operations. Throughout this
|
||
|
// class this is the only type that is used to store, retrieve and manipulate
|
||
|
// the bits. When returning a TimeTicks value outside this class it's
|
||
|
// necessary to run the proper checks to insure correctness of the conversion
|
||
|
// that has to go through int_64t. (See DeadlineFromBits()).
|
||
|
using BitsType = uint64_t;
|
||
|
static_assert(std::is_same_v<std::underlying_type<Flag>::type, BitsType>,
|
||
|
"Flag should have the same underlying type as bits_ to "
|
||
|
"simplify thinking about bit operations");
|
||
|
|
||
|
// Holds the bits of both the flags and the TimeTicks deadline.
|
||
|
// TimeTicks values represent a count of microseconds since boot which may or
|
||
|
// may not include suspend time depending on the platform. Using the seven
|
||
|
// highest order bits and the sign bit to store flags still enables the
|
||
|
// storing of TimeTicks values that can represent up to ~1142 years of uptime
|
||
|
// in the remaining bits. Should never be directly accessed from outside the
|
||
|
// class. Starts out at Max() to provide a base-line deadline that will not be
|
||
|
// reached during normal execution.
|
||
|
//
|
||
|
// Binary format: 0xFFDDDDDDDDDDDDDDDD
|
||
|
// F = Flags
|
||
|
// D = Deadline
|
||
|
std::atomic<BitsType> bits_{static_cast<uint64_t>(Max().ToInternalValue())};
|
||
|
|
||
|
RepeatingCallback<uint64_t(void)> switch_bits_callback_for_testing_;
|
||
|
|
||
|
THREAD_CHECKER(thread_checker_);
|
||
|
|
||
|
FRIEND_TEST_ALL_PREFIXES(HangWatchDeadlineTest, BitsPreservedThroughExtract);
|
||
|
};
|
||
|
|
||
|
// Contains the information necessary for hang watching a specific
|
||
|
// thread. Instances of this class are accessed concurrently by the associated
|
||
|
// thread and the HangWatcher. The HangWatcher owns instances of this
|
||
|
// class and outside of it they are accessed through
|
||
|
// GetHangWatchStateForCurrentThread().
|
||
|
class BASE_EXPORT HangWatchState {
|
||
|
public:
|
||
|
// |thread_type| is the type of thread the watch state will
|
||
|
// be associated with. It's the responsibility of the creating
|
||
|
// code to choose the correct type.
|
||
|
explicit HangWatchState(HangWatcher::ThreadType thread_type);
|
||
|
~HangWatchState();
|
||
|
|
||
|
HangWatchState(const HangWatchState&) = delete;
|
||
|
HangWatchState& operator=(const HangWatchState&) = delete;
|
||
|
|
||
|
// Allocates a new state object bound to the calling thread and returns an
|
||
|
// owning pointer to it.
|
||
|
static std::unique_ptr<HangWatchState> CreateHangWatchStateForCurrentThread(
|
||
|
HangWatcher::ThreadType thread_type);
|
||
|
|
||
|
// Retrieves the hang watch state associated with the calling thread.
|
||
|
// Returns nullptr if no HangWatchState exists for the current thread (see
|
||
|
// CreateHangWatchStateForCurrentThread()).
|
||
|
static HangWatchState* GetHangWatchStateForCurrentThread();
|
||
|
|
||
|
// Returns the current deadline. Use this function if you need to
|
||
|
// store the value. To test if the deadline has expired use IsOverDeadline().
|
||
|
// WARNING: The deadline and flags can change concurrently. If you need to
|
||
|
// inspect both you need to use GetFlagsAndDeadline() to get a coherent
|
||
|
// race-free view of the state.
|
||
|
TimeTicks GetDeadline() const;
|
||
|
|
||
|
// Returns a mask containing the hang watching flags and the value as a pair.
|
||
|
// Use to inspect the flags and deadline and optionally call
|
||
|
// SetShouldBlockOnHang(flags, deadline).
|
||
|
std::pair<uint64_t, TimeTicks> GetFlagsAndDeadline() const;
|
||
|
|
||
|
// Sets the deadline to a new value.
|
||
|
void SetDeadline(TimeTicks deadline);
|
||
|
|
||
|
// Mark this thread as ignored for hang watching. This means existing
|
||
|
// WatchHangsInScope will not trigger hangs.
|
||
|
void SetIgnoreCurrentWatchHangsInScope();
|
||
|
|
||
|
// Reactivate hang watching on this thread. Should be called when all
|
||
|
// WatchHangsInScope instances that were ignored have completed.
|
||
|
void UnsetIgnoreCurrentWatchHangsInScope();
|
||
|
|
||
|
// Mark the current state as having to block in its destruction until hang
|
||
|
// capture completes.
|
||
|
bool SetShouldBlockOnHang(uint64_t old_flags, TimeTicks old_deadline);
|
||
|
|
||
|
// Returns true if |flag| is set and false if not. WARNING: The deadline and
|
||
|
// flags can change concurrently. If you need to inspect both you need to use
|
||
|
// GetFlagsAndDeadline() to get a coherent race-free view of the state.
|
||
|
bool IsFlagSet(HangWatchDeadline::Flag flag);
|
||
|
|
||
|
// Tests whether the associated thread's execution has gone over the deadline.
|
||
|
bool IsOverDeadline() const;
|
||
|
|
||
|
#if DCHECK_IS_ON()
|
||
|
// Saves the supplied WatchHangsInScope as the currently active
|
||
|
// WatchHangsInScope.
|
||
|
void SetCurrentWatchHangsInScope(WatchHangsInScope* scope);
|
||
|
|
||
|
// Retrieve the currently active scope.
|
||
|
WatchHangsInScope* GetCurrentWatchHangsInScope();
|
||
|
#endif
|
||
|
|
||
|
PlatformThreadId GetThreadID() const;
|
||
|
|
||
|
// Retrieve the current hang watch deadline directly. For testing only.
|
||
|
HangWatchDeadline* GetHangWatchDeadlineForTesting();
|
||
|
|
||
|
// Returns the current nesting level.
|
||
|
int nesting_level() { return nesting_level_; }
|
||
|
|
||
|
// Increase the nesting level by 1;
|
||
|
void IncrementNestingLevel();
|
||
|
|
||
|
// Reduce the nesting level by 1;
|
||
|
void DecrementNestingLevel();
|
||
|
|
||
|
// Returns the type of the thread under watch.
|
||
|
HangWatcher::ThreadType thread_type() const { return thread_type_; }
|
||
|
|
||
|
private:
|
||
|
// The thread that creates the instance should be the class that updates
|
||
|
// the deadline.
|
||
|
THREAD_CHECKER(thread_checker_);
|
||
|
|
||
|
const AutoReset<HangWatchState*> resetter_;
|
||
|
|
||
|
// If the deadline fails to be updated before TimeTicks::Now() ever
|
||
|
// reaches the value contained in it this constistutes a hang.
|
||
|
HangWatchDeadline deadline_;
|
||
|
|
||
|
// A unique ID of the thread under watch. Used for logging in crash reports
|
||
|
// only.
|
||
|
PlatformThreadId thread_id_;
|
||
|
|
||
|
// Number of active HangWatchScopeEnables on this thread.
|
||
|
int nesting_level_ = 0;
|
||
|
|
||
|
// The type of the thread under watch.
|
||
|
const HangWatcher::ThreadType thread_type_;
|
||
|
|
||
|
#if DCHECK_IS_ON()
|
||
|
// Used to keep track of the current WatchHangsInScope and detect improper
|
||
|
// usage. Scopes should always be destructed in reverse order from the one
|
||
|
// they were constructed in. Example of improper use:
|
||
|
//
|
||
|
// {
|
||
|
// std::unique_ptr<Scope> scope = std::make_unique<Scope>(...);
|
||
|
// Scope other_scope;
|
||
|
// |scope| gets deallocated first, violating reverse destruction order.
|
||
|
// scope.reset();
|
||
|
// }
|
||
|
raw_ptr<WatchHangsInScope> current_watch_hangs_in_scope_{nullptr};
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
} // namespace internal
|
||
|
} // namespace base
|
||
|
|
||
|
#endif // BASE_THREADING_HANG_WATCHER_H_
|