Index: base/win/sampling_profiler.cc |
diff --git a/base/win/sampling_profiler.cc b/base/win/sampling_profiler.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..268542a94fd8b57f6fa600b93879d579feb37d53 |
--- /dev/null |
+++ b/base/win/sampling_profiler.cc |
@@ -0,0 +1,232 @@ |
+// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/win/sampling_profiler.h" |
+ |
+#include <winternl.h> // for NTSTATUS. |
+ |
+#include "base/lazy_instance.h" |
+ |
+// Copied from wdm.h in the WDK as we don't want to take |
+// a dependency on the WDK. |
+typedef enum _KPROFILE_SOURCE { |
+ ProfileTime, |
+ ProfileAlignmentFixup, |
+ ProfileTotalIssues, |
+ ProfilePipelineDry, |
+ ProfileLoadInstructions, |
+ ProfilePipelineFrozen, |
+ ProfileBranchInstructions, |
+ ProfileTotalNonissues, |
+ ProfileDcacheMisses, |
+ ProfileIcacheMisses, |
+ ProfileCacheMisses, |
+ ProfileBranchMispredictions, |
+ ProfileStoreInstructions, |
+ ProfileFpInstructions, |
+ ProfileIntegerInstructions, |
+ Profile2Issue, |
+ Profile3Issue, |
+ Profile4Issue, |
+ ProfileSpecialInstructions, |
+ ProfileTotalCycles, |
+ ProfileIcacheIssues, |
+ ProfileDcacheAccesses, |
+ ProfileMemoryBarrierCycles, |
+ ProfileLoadLinkedIssues, |
+ ProfileMaximum |
+} KPROFILE_SOURCE; |
+ |
+ |
+namespace { |
+ |
+// Signatures for the native functions we need to access the sampling profiler. |
+typedef NTSTATUS (NTAPI *ZwSetIntervalProfileFunc)(ULONG, KPROFILE_SOURCE); |
+typedef NTSTATUS (NTAPI *ZwQueryIntervalProfileFunc)(KPROFILE_SOURCE, PULONG); |
+ |
+typedef NTSTATUS (NTAPI *ZwCreateProfileFunc)(PHANDLE profile, |
+ HANDLE process, |
+ PVOID code_start, |
+ ULONG code_size, |
+ ULONG eip_bucket_shift, |
+ PULONG buckets, |
+ ULONG buckets_byte_size, |
+ KPROFILE_SOURCE source, |
+ DWORD_PTR processor_mask); |
+ |
+typedef NTSTATUS (NTAPI *ZwStartProfileFunc)(HANDLE); |
+typedef NTSTATUS (NTAPI *ZwStopProfileFunc)(HANDLE); |
+ |
+// This class is used to lazy-initialize pointers to the native |
+// functions we need to access. |
+class ProfilerFuncs { |
+ public: |
+ ProfilerFuncs(); |
+ |
+ ZwSetIntervalProfileFunc ZwSetIntervalProfile; |
+ ZwQueryIntervalProfileFunc ZwQueryIntervalProfile; |
+ ZwCreateProfileFunc ZwCreateProfile; |
+ ZwStartProfileFunc ZwStartProfile; |
+ ZwStopProfileFunc ZwStopProfile; |
+ |
+ // True iff all of the function pointers above were successfully initialized. |
+ bool initialized_; |
+}; |
+ |
+ProfilerFuncs::ProfilerFuncs() : initialized_(false) { |
+ HMODULE ntdll = ::GetModuleHandle(L"ntdll.dll"); |
+ if (ntdll != NULL) { |
+ ZwSetIntervalProfile = reinterpret_cast<ZwSetIntervalProfileFunc>( |
+ ::GetProcAddress(ntdll, "ZwSetIntervalProfile")); |
+ ZwQueryIntervalProfile = reinterpret_cast<ZwQueryIntervalProfileFunc>( |
+ ::GetProcAddress(ntdll, "ZwQueryIntervalProfile")); |
+ ZwCreateProfile = reinterpret_cast<ZwCreateProfileFunc>( |
+ ::GetProcAddress(ntdll, "ZwCreateProfile")); |
+ ZwStartProfile = reinterpret_cast<ZwStartProfileFunc>( |
+ ::GetProcAddress(ntdll, "ZwStartProfile")); |
+ ZwStopProfile = reinterpret_cast<ZwStopProfileFunc>( |
+ ::GetProcAddress(ntdll, "ZwStopProfile")); |
+ |
+ if (ZwSetIntervalProfile && |
+ ZwQueryIntervalProfile && |
+ ZwCreateProfile && |
+ ZwStartProfile && |
+ ZwStopProfile) { |
+ initialized_ = true; |
+ } |
+ } |
+} |
+ |
+base::LazyInstance<ProfilerFuncs, base::LeakyLazyInstanceTraits<ProfilerFuncs>> |
+ funcs = LAZY_INSTANCE_INITIALIZER; |
+ |
+} // namespace |
+ |
+ |
+namespace base { |
+namespace win { |
+ |
+SamplingProfiler::SamplingProfiler() : is_started_(false) { |
+} |
+ |
+SamplingProfiler::~SamplingProfiler() { |
+ if (is_started_) { |
+ CHECK(Stop()) << |
+ "Unable to stop sampling profiler, this will cause memory corruption."; |
+ } |
+} |
+ |
+bool SamplingProfiler::Initialize(HANDLE process, |
+ void* start, |
+ size_t size, |
+ size_t log2_bucket_size) { |
+ // You only get to initialize each instance once. |
+ DCHECK(!profile_handle_.IsValid()); |
+ DCHECK(!is_started_); |
+ DCHECK(start != NULL); |
+ DCHECK_NE(0U, size); |
+ DCHECK_LE(2, log2_bucket_size); |
+ DCHECK_GE(32, log2_bucket_size); |
+ |
+ // Bail if the native functions weren't found. |
+ if (!funcs.Get().initialized_) |
+ return false; |
+ |
+ size_t bucket_size = 1 << log2_bucket_size; |
+ size_t num_buckets = (size + bucket_size - 1) / bucket_size; |
+ DCHECK(num_buckets != 0); |
+ buckets_.resize(num_buckets); |
+ |
+ // Get our affinity mask for the call below. |
+ DWORD_PTR process_affinity = 0; |
+ DWORD_PTR system_affinity = 0; |
+ if (!::GetProcessAffinityMask(process, &process_affinity, &system_affinity)) { |
+ LOG(ERROR) << "Failed to get process affinity mask."; |
+ return false; |
+ } |
+ |
+ HANDLE profile = NULL; |
+ NTSTATUS status = |
+ funcs.Get().ZwCreateProfile(&profile, |
+ process, |
+ start, |
+ size, |
+ log2_bucket_size, |
+ &buckets_[0], |
+ sizeof(buckets_[0]) * num_buckets, |
+ ProfileTime, |
+ process_affinity); |
+ |
+ if (!NT_SUCCESS(status)) { |
+ // Might as well deallocate the buckets. |
+ buckets_.resize(0); |
+ LOG(ERROR) << "Failed to create profile, error 0x" << std::hex << status; |
+ return false; |
+ } |
+ |
+ DCHECK(profile != NULL); |
+ profile_handle_.Set(profile); |
+ |
+ return true; |
+} |
+ |
+bool SamplingProfiler::Start() { |
+ DCHECK(profile_handle_.IsValid()); |
+ DCHECK(!is_started_); |
+ DCHECK(funcs.Get().initialized_); |
+ |
+ NTSTATUS status = funcs.Get().ZwStartProfile(profile_handle_.Get()); |
+ if (!NT_SUCCESS(status)) |
+ return false; |
+ |
+ is_started_ = true; |
+ |
+ return true; |
+} |
+ |
+bool SamplingProfiler::Stop() { |
+ DCHECK(profile_handle_.IsValid()); |
+ DCHECK(is_started_); |
+ DCHECK(funcs.Get().initialized_); |
+ |
+ NTSTATUS status = funcs.Get().ZwStopProfile(profile_handle_.Get()); |
+ if (!NT_SUCCESS(status)) |
+ return false; |
+ is_started_ = false; |
+ |
+ return true; |
+} |
+ |
+bool SamplingProfiler::SetSamplingInterval(base::TimeDelta sampling_interval) { |
+ if (!funcs.Get().initialized_) |
+ return false; |
+ |
+ // According to Nebbet, the sampling interval is in units of 100ns. |
+ ULONG interval = sampling_interval.InMicroseconds() * 10; |
+ NTSTATUS status = funcs.Get().ZwSetIntervalProfile(interval, ProfileTime); |
+ if (!NT_SUCCESS(status)) |
+ return false; |
+ |
+ return true; |
+} |
+ |
+bool SamplingProfiler::GetSamplingInterval(base::TimeDelta* sampling_interval) { |
+ DCHECK(sampling_interval != NULL); |
+ |
+ if (!funcs.Get().initialized_) |
+ return false; |
+ |
+ ULONG interval = 0; |
+ NTSTATUS status = funcs.Get().ZwQueryIntervalProfile(ProfileTime, &interval); |
+ if (!NT_SUCCESS(status)) |
+ return false; |
+ |
+ // According to Nebbet, the sampling interval is in units of 100ns. |
+ *sampling_interval = base::TimeDelta::FromMicroseconds(interval / 10); |
+ |
+ return true; |
+} |
+ |
+} // namespace win |
+} // namespace base |