OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "base/win/sampling_profiler.h" |
| 6 |
| 7 #include <winternl.h> // for NTSTATUS. |
| 8 |
| 9 #include "base/lazy_instance.h" |
| 10 |
| 11 // Copied from wdm.h in the WDK as we don't want to take |
| 12 // a dependency on the WDK. |
| 13 typedef enum _KPROFILE_SOURCE { |
| 14 ProfileTime, |
| 15 ProfileAlignmentFixup, |
| 16 ProfileTotalIssues, |
| 17 ProfilePipelineDry, |
| 18 ProfileLoadInstructions, |
| 19 ProfilePipelineFrozen, |
| 20 ProfileBranchInstructions, |
| 21 ProfileTotalNonissues, |
| 22 ProfileDcacheMisses, |
| 23 ProfileIcacheMisses, |
| 24 ProfileCacheMisses, |
| 25 ProfileBranchMispredictions, |
| 26 ProfileStoreInstructions, |
| 27 ProfileFpInstructions, |
| 28 ProfileIntegerInstructions, |
| 29 Profile2Issue, |
| 30 Profile3Issue, |
| 31 Profile4Issue, |
| 32 ProfileSpecialInstructions, |
| 33 ProfileTotalCycles, |
| 34 ProfileIcacheIssues, |
| 35 ProfileDcacheAccesses, |
| 36 ProfileMemoryBarrierCycles, |
| 37 ProfileLoadLinkedIssues, |
| 38 ProfileMaximum |
| 39 } KPROFILE_SOURCE; |
| 40 |
| 41 |
| 42 namespace { |
| 43 |
| 44 // Signatures for the native functions we need to access the sampling profiler. |
| 45 typedef NTSTATUS (NTAPI *ZwSetIntervalProfileFunc)(ULONG, KPROFILE_SOURCE); |
| 46 typedef NTSTATUS (NTAPI *ZwQueryIntervalProfileFunc)(KPROFILE_SOURCE, PULONG); |
| 47 |
| 48 typedef NTSTATUS (NTAPI *ZwCreateProfileFunc)(PHANDLE profile, |
| 49 HANDLE process, |
| 50 PVOID code_start, |
| 51 ULONG code_size, |
| 52 ULONG eip_bucket_shift, |
| 53 PULONG buckets, |
| 54 ULONG buckets_byte_size, |
| 55 KPROFILE_SOURCE source, |
| 56 DWORD_PTR processor_mask); |
| 57 |
| 58 typedef NTSTATUS (NTAPI *ZwStartProfileFunc)(HANDLE); |
| 59 typedef NTSTATUS (NTAPI *ZwStopProfileFunc)(HANDLE); |
| 60 |
| 61 // This class is used to lazy-initialize pointers to the native |
| 62 // functions we need to access. |
| 63 class ProfilerFuncs { |
| 64 public: |
| 65 ProfilerFuncs(); |
| 66 |
| 67 ZwSetIntervalProfileFunc ZwSetIntervalProfile; |
| 68 ZwQueryIntervalProfileFunc ZwQueryIntervalProfile; |
| 69 ZwCreateProfileFunc ZwCreateProfile; |
| 70 ZwStartProfileFunc ZwStartProfile; |
| 71 ZwStopProfileFunc ZwStopProfile; |
| 72 |
| 73 // True iff all of the function pointers above were successfully initialized. |
| 74 bool initialized_; |
| 75 }; |
| 76 |
| 77 ProfilerFuncs::ProfilerFuncs() : initialized_(false) { |
| 78 HMODULE ntdll = ::GetModuleHandle(L"ntdll.dll"); |
| 79 if (ntdll != NULL) { |
| 80 ZwSetIntervalProfile = reinterpret_cast<ZwSetIntervalProfileFunc>( |
| 81 ::GetProcAddress(ntdll, "ZwSetIntervalProfile")); |
| 82 ZwQueryIntervalProfile = reinterpret_cast<ZwQueryIntervalProfileFunc>( |
| 83 ::GetProcAddress(ntdll, "ZwQueryIntervalProfile")); |
| 84 ZwCreateProfile = reinterpret_cast<ZwCreateProfileFunc>( |
| 85 ::GetProcAddress(ntdll, "ZwCreateProfile")); |
| 86 ZwStartProfile = reinterpret_cast<ZwStartProfileFunc>( |
| 87 ::GetProcAddress(ntdll, "ZwStartProfile")); |
| 88 ZwStopProfile = reinterpret_cast<ZwStopProfileFunc>( |
| 89 ::GetProcAddress(ntdll, "ZwStopProfile")); |
| 90 |
| 91 if (ZwSetIntervalProfile && |
| 92 ZwQueryIntervalProfile && |
| 93 ZwCreateProfile && |
| 94 ZwStartProfile && |
| 95 ZwStopProfile) { |
| 96 initialized_ = true; |
| 97 } |
| 98 } |
| 99 } |
| 100 |
| 101 base::LazyInstance<ProfilerFuncs, base::LeakyLazyInstanceTraits<ProfilerFuncs>> |
| 102 funcs = LAZY_INSTANCE_INITIALIZER; |
| 103 |
| 104 } // namespace |
| 105 |
| 106 |
| 107 namespace base { |
| 108 namespace win { |
| 109 |
| 110 SamplingProfiler::SamplingProfiler() : is_started_(false) { |
| 111 } |
| 112 |
| 113 SamplingProfiler::~SamplingProfiler() { |
| 114 if (is_started_) { |
| 115 CHECK(Stop()) << |
| 116 "Unable to stop sampling profiler, this will cause memory corruption."; |
| 117 } |
| 118 } |
| 119 |
| 120 bool SamplingProfiler::Initialize(HANDLE process, |
| 121 void* start, |
| 122 size_t size, |
| 123 size_t log2_bucket_size) { |
| 124 // You only get to initialize each instance once. |
| 125 DCHECK(!profile_handle_.IsValid()); |
| 126 DCHECK(!is_started_); |
| 127 DCHECK(start != NULL); |
| 128 DCHECK_NE(0U, size); |
| 129 DCHECK_LE(2, log2_bucket_size); |
| 130 DCHECK_GE(32, log2_bucket_size); |
| 131 |
| 132 // Bail if the native functions weren't found. |
| 133 if (!funcs.Get().initialized_) |
| 134 return false; |
| 135 |
| 136 size_t bucket_size = 1 << log2_bucket_size; |
| 137 size_t num_buckets = (size + bucket_size - 1) / bucket_size; |
| 138 DCHECK(num_buckets != 0); |
| 139 buckets_.resize(num_buckets); |
| 140 |
| 141 // Get our affinity mask for the call below. |
| 142 DWORD_PTR process_affinity = 0; |
| 143 DWORD_PTR system_affinity = 0; |
| 144 if (!::GetProcessAffinityMask(process, &process_affinity, &system_affinity)) { |
| 145 LOG(ERROR) << "Failed to get process affinity mask."; |
| 146 return false; |
| 147 } |
| 148 |
| 149 HANDLE profile = NULL; |
| 150 NTSTATUS status = |
| 151 funcs.Get().ZwCreateProfile(&profile, |
| 152 process, |
| 153 start, |
| 154 size, |
| 155 log2_bucket_size, |
| 156 &buckets_[0], |
| 157 sizeof(buckets_[0]) * num_buckets, |
| 158 ProfileTime, |
| 159 process_affinity); |
| 160 |
| 161 if (!NT_SUCCESS(status)) { |
| 162 // Might as well deallocate the buckets. |
| 163 buckets_.resize(0); |
| 164 LOG(ERROR) << "Failed to create profile, error 0x" << std::hex << status; |
| 165 return false; |
| 166 } |
| 167 |
| 168 DCHECK(profile != NULL); |
| 169 profile_handle_.Set(profile); |
| 170 |
| 171 return true; |
| 172 } |
| 173 |
| 174 bool SamplingProfiler::Start() { |
| 175 DCHECK(profile_handle_.IsValid()); |
| 176 DCHECK(!is_started_); |
| 177 DCHECK(funcs.Get().initialized_); |
| 178 |
| 179 NTSTATUS status = funcs.Get().ZwStartProfile(profile_handle_.Get()); |
| 180 if (!NT_SUCCESS(status)) |
| 181 return false; |
| 182 |
| 183 is_started_ = true; |
| 184 |
| 185 return true; |
| 186 } |
| 187 |
| 188 bool SamplingProfiler::Stop() { |
| 189 DCHECK(profile_handle_.IsValid()); |
| 190 DCHECK(is_started_); |
| 191 DCHECK(funcs.Get().initialized_); |
| 192 |
| 193 NTSTATUS status = funcs.Get().ZwStopProfile(profile_handle_.Get()); |
| 194 if (!NT_SUCCESS(status)) |
| 195 return false; |
| 196 is_started_ = false; |
| 197 |
| 198 return true; |
| 199 } |
| 200 |
| 201 bool SamplingProfiler::SetSamplingInterval(base::TimeDelta sampling_interval) { |
| 202 if (!funcs.Get().initialized_) |
| 203 return false; |
| 204 |
| 205 // According to Nebbet, the sampling interval is in units of 100ns. |
| 206 ULONG interval = sampling_interval.InMicroseconds() * 10; |
| 207 NTSTATUS status = funcs.Get().ZwSetIntervalProfile(interval, ProfileTime); |
| 208 if (!NT_SUCCESS(status)) |
| 209 return false; |
| 210 |
| 211 return true; |
| 212 } |
| 213 |
| 214 bool SamplingProfiler::GetSamplingInterval(base::TimeDelta* sampling_interval) { |
| 215 DCHECK(sampling_interval != NULL); |
| 216 |
| 217 if (!funcs.Get().initialized_) |
| 218 return false; |
| 219 |
| 220 ULONG interval = 0; |
| 221 NTSTATUS status = funcs.Get().ZwQueryIntervalProfile(ProfileTime, &interval); |
| 222 if (!NT_SUCCESS(status)) |
| 223 return false; |
| 224 |
| 225 // According to Nebbet, the sampling interval is in units of 100ns. |
| 226 *sampling_interval = base::TimeDelta::FromMicroseconds(interval / 10); |
| 227 |
| 228 return true; |
| 229 } |
| 230 |
| 231 } // namespace win |
| 232 } // namespace base |
OLD | NEW |