| // Copyright 2025 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_PLATFORM_THREAD_METRICS_H_ |
| #define BASE_THREADING_PLATFORM_THREAD_METRICS_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <mutex> |
| #include <optional> |
| #include <string> |
| #include <string_view> |
| |
| #include "base/base_export.h" |
| #include "base/functional/callback_forward.h" |
| #include "base/metrics/histogram.h" |
| #include "base/synchronization/lock.h" |
| #include "base/thread_annotations.h" |
| #include "base/threading/platform_thread.h" |
| #include "base/time/time.h" |
| #include "base/timer/timer.h" |
| #include "build/build_config.h" |
| |
| #if BUILDFLAG(IS_WIN) |
| #include "base/win/scoped_handle.h" |
| #endif |
| |
| namespace base { |
| |
| #if BUILDFLAG(IS_ANDROID) |
| // Forward declaration |
| template <typename T> |
| class NoDestructor; |
| class PlatformThreadPriorityMonitorTest; |
| #endif // BUILDFLAG(IS_ANDROID) |
| |
| // Used to log metrics about a specific thread. Modeled on ProcessMetrics. |
| // |
| // On Android and Fuchsia the thread must belong to the current process. (This |
| // is due to the way Chrome spawns processes on Android, so uses of |
| // PlatformThreadMetrics outside of Chrome might not have this restriction.) |
| // |
| // CPU usage of the current thread can also be measured with base::ThreadTicks. |
| // However on most platforms ThreadTicks can't measure other threads. |
| // |
| // Depending on platform, measuring CPU usage needs either a thread ID (TID) or |
| // a thread handle: |
| // |
| // * Apple needs a thread handle, and there's no obvious way to convert a TID to |
| // a handle, so it only supports CreateFromHandle(). |
| // * Windows needs a thread handle, but it's possible to convert back and forth |
| // between TID's and handles, so it supports both CreateFromHandle() and |
| // CreateFromId(). |
| // * Every other platform needs a TID, and there's no obvious way to convert a |
| // handle to a TID, so they only support CreateFromId(). |
| // |
| // TODO(crbug.com/420681350): There might be ways to support CreateFromHandle() |
| // and threads in other processes on Fuchsia. Improve the Fuchsia implementation |
| // if there's any demand. |
| class BASE_EXPORT PlatformThreadMetrics { |
| public: |
| static std::unique_ptr<PlatformThreadMetrics> CreateForCurrentThread(); |
| |
| #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) |
| static std::unique_ptr<PlatformThreadMetrics> CreateFromHandle( |
| PlatformThreadHandle handle); |
| #endif |
| |
| #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || \ |
| BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) |
| static std::unique_ptr<PlatformThreadMetrics> CreateFromId( |
| PlatformThreadId tid); |
| #endif |
| |
| PlatformThreadMetrics(const PlatformThreadMetrics&) = delete; |
| PlatformThreadMetrics& operator=(const PlatformThreadMetrics&) = delete; |
| |
| ~PlatformThreadMetrics(); |
| |
| // Returns the cumulative CPU usage of the thread since process start, or |
| // nullopt on error. Unlike ProcessMetrics::GetCumulativeCPUUsage() this |
| // doesn't measure multiple concurrent threads, so it shouldn't return more |
| // than 1 second of CPU utilization per 1 wall-clock second. However due to |
| // rounding it may report slightly higher values under heavy load. |
| std::optional<TimeDelta> GetCumulativeCPUUsage(); |
| |
| // Returns the percentage of time spent executing the thread in the interval |
| // since the last time the method was called, using the current |
| // `cumulative_cpu`. Equivalent to ProcessMetrics:: |
| // GetPlatformIndependentCPUUsage(), but this doesn't measure multiple |
| // concurrent threads, so it should always return a value in the range 0% to |
| // 100%. However due to rounding it may report slightly more than 100% under |
| // heavy load. |
| // |
| // Since this API measures usage over an interval, it will return zero on the |
| // first call, and an actual value only on the second and subsequent calls. |
| double GetCPUUsageProportion(TimeDelta cumulative_cpu); |
| |
| // Same as the above, but automatically calls GetCumulativeCPUUsage() to |
| // determine the current cumulative CPU. Returns nullopt if |
| // GetCumulativeCPUUsage() fails. |
| std::optional<double> GetCPUUsageProportion(); |
| |
| private: |
| #if BUILDFLAG(IS_APPLE) |
| explicit PlatformThreadMetrics(PlatformThreadHandle handle) |
| : handle_(handle) {} |
| |
| PlatformThreadHandle handle_; |
| #elif BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || \ |
| BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) |
| explicit PlatformThreadMetrics(PlatformThreadId tid) : tid_(tid) {} |
| |
| PlatformThreadId tid_; |
| #elif BUILDFLAG(IS_WIN) |
| explicit PlatformThreadMetrics(win::ScopedHandle&& handle); |
| |
| win::ScopedHandle handle_; |
| #endif |
| |
| // Previous measurements used to compute the CPU usage between calls to |
| // GetCPUUsageProportion(). |
| std::optional<TimeTicks> last_cpu_time_; |
| TimeDelta last_cumulative_cpu_; |
| }; |
| |
| #if BUILDFLAG(IS_ANDROID) |
| // A class to monitor thread priorities on Android. |
| class BASE_EXPORT PlatformThreadPriorityMonitor { |
| public: |
| // Gets the singleton instance of the monitor. |
| static PlatformThreadPriorityMonitor& Get(); |
| |
| // Registers the current thread for priority monitoring. A histogram named |
| // "Scheduling.ThreadPriority.<Process>.<suffix>" will be created to record |
| // the thread's nice value. The thread will be unregistered automatically on |
| // join. |
| void RegisterCurrentThread(const std::string_view suffix); |
| |
| // Removes the current thread from priority monitoring. |
| void UnregisterCurrentThread(); |
| |
| // Start recording the current priority (nice value) of all registered threads |
| // to their respective histograms. Sampled every 5 minutes. |
| void Start(); |
| |
| private: |
| friend class NoDestructor<PlatformThreadPriorityMonitor>; |
| friend class PlatformThreadPriorityMonitorTest; |
| |
| // The mean interval between two consecutive recordings of thread |
| // priorities. |
| static constexpr TimeDelta kMinSamplingInterval = Minutes(5); |
| |
| PlatformThreadPriorityMonitor(); |
| ~PlatformThreadPriorityMonitor(); |
| void RecordThreadPriorities(); |
| std::string GetHistogramNameForSuffix(const std::string_view suffix); |
| static void ScheduleRecordingTask(); |
| |
| Lock lock_; |
| // Maps a thread ID to the histogram used to record its priority. |
| std::map<PlatformThreadId, raw_ptr<HistogramBase>> thread_id_to_histogram_ |
| GUARDED_BY(lock_); |
| std::optional<TimeTicks> next_reporting_time_ GUARDED_BY(lock_); |
| |
| std::once_flag once_flag_; |
| const std::string process_name_; |
| }; |
| |
| #endif // BUILDFLAG(IS_ANDROID) |
| |
| } // namespace base |
| |
| #endif // BASE_THREADING_PLATFORM_THREAD_METRICS_H_ |