| // 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. |
| |
| #include "base/threading/platform_thread_metrics.h" |
| |
| #include <windows.h> |
| |
| #include <memory> |
| #include <optional> |
| #include <utility> |
| |
| #include "base/memory/ptr_util.h" |
| #include "base/threading/platform_thread.h" |
| #include "base/time/time.h" |
| #include "base/trace_event/trace_event.h" |
| #include "base/win/scoped_handle.h" |
| #include "build/build_config.h" |
| |
| namespace base { |
| |
| namespace { |
| |
| std::optional<TimeDelta> GetImpreciseCumulativeCPUUsage( |
| const win::ScopedHandle& handle) { |
| FILETIME creation_time; |
| FILETIME exit_time; |
| FILETIME kernel_time; |
| FILETIME user_time; |
| |
| if (!::GetThreadTimes(handle.get(), &creation_time, &exit_time, &kernel_time, |
| &user_time)) { |
| return std::nullopt; |
| } |
| |
| return TimeDelta::FromFileTime(kernel_time) + |
| TimeDelta::FromFileTime(user_time); |
| } |
| |
| } // namespace |
| |
| // static |
| std::unique_ptr<PlatformThreadMetrics> PlatformThreadMetrics::CreateFromHandle( |
| PlatformThreadHandle handle) { |
| // Duplicate the handle to get one that can be owned by PlatformThreadMetrics. |
| HANDLE new_handle = INVALID_HANDLE_VALUE; |
| if (!::DuplicateHandle(/*hSourceProcessHandle=*/::GetCurrentProcess(), |
| /*hSourceHandle=*/handle.platform_handle(), |
| /*hTargetProcessHandle=*/::GetCurrentProcess(), |
| &new_handle, THREAD_QUERY_LIMITED_INFORMATION, |
| /*bInheritHandle=*/FALSE, /*dwOptions=*/0)) { |
| return nullptr; |
| } |
| return WrapUnique(new PlatformThreadMetrics(win::ScopedHandle(new_handle))); |
| } |
| |
| // static |
| std::unique_ptr<PlatformThreadMetrics> PlatformThreadMetrics::CreateFromId( |
| PlatformThreadId tid) { |
| if (tid == kInvalidThreadId) { |
| return nullptr; |
| } |
| win::ScopedHandle handle(::OpenThread(THREAD_QUERY_LIMITED_INFORMATION, |
| /*bInheritHandle=*/FALSE, tid.raw())); |
| if (!handle.is_valid()) { |
| return nullptr; |
| } |
| |
| return WrapUnique(new PlatformThreadMetrics(std::move(handle))); |
| } |
| |
| PlatformThreadMetrics::PlatformThreadMetrics(win::ScopedHandle&& handle) |
| : handle_(std::move(handle)) {} |
| |
| std::optional<TimeDelta> PlatformThreadMetrics::GetCumulativeCPUUsage() { |
| TRACE_EVENT("base", "Thread::GetCumulativeCPUUsage"); |
| #if defined(ARCH_CPU_ARM64) |
| // Precise CPU usage is not available on Arm CPUs because they don't support |
| // constant rate TSC. |
| return GetImpreciseCumulativeCPUUsage(handle_); |
| #else // !defined(ARCH_CPU_ARM64) |
| if (!time_internal::HasConstantRateTSC()) { |
| return GetImpreciseCumulativeCPUUsage(handle_); |
| } |
| |
| const double tsc_ticks_per_second = time_internal::TSCTicksPerSecond(); |
| if (tsc_ticks_per_second == 0) { |
| // TSC is only initialized once TSCTicksPerSecond() is called twice 50 ms |
| // apart on the same thread to get a baseline. In unit tests, it is frequent |
| // for the initialization not to be complete. In production, it can also |
| // theoretically happen. |
| return GetImpreciseCumulativeCPUUsage(handle_); |
| } |
| |
| ULONG64 cycle_time = 0; |
| if (!::QueryThreadCycleTime(handle_.get(), &cycle_time)) { |
| return std::nullopt; |
| } |
| |
| return Seconds(cycle_time / tsc_ticks_per_second); |
| #endif // !defined(ARCH_CPU_ARM64) |
| } |
| |
| } // namespace base |