Webkit roll 143935:143980
[chromium-blink-merge.git] / base / win / sampling_profiler.cc
blob150452c447aefde8da10f71f145a3d3a21617af8
1 // Copyright (c) 2012 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.
5 #include "base/win/sampling_profiler.h"
7 #include <winternl.h> // for NTSTATUS.
9 #include "base/lazy_instance.h"
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;
42 namespace {
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);
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);
58 typedef NTSTATUS (NTAPI *ZwStartProfileFunc)(HANDLE);
59 typedef NTSTATUS (NTAPI *ZwStopProfileFunc)(HANDLE);
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();
67 ZwSetIntervalProfileFunc ZwSetIntervalProfile;
68 ZwQueryIntervalProfileFunc ZwQueryIntervalProfile;
69 ZwCreateProfileFunc ZwCreateProfile;
70 ZwStartProfileFunc ZwStartProfile;
71 ZwStopProfileFunc ZwStopProfile;
73 // True iff all of the function pointers above were successfully initialized.
74 bool initialized_;
77 ProfilerFuncs::ProfilerFuncs()
78 : ZwSetIntervalProfile(NULL),
79 ZwQueryIntervalProfile(NULL),
80 ZwCreateProfile(NULL),
81 ZwStartProfile(NULL),
82 ZwStopProfile(NULL),
83 initialized_(false) {
84 HMODULE ntdll = ::GetModuleHandle(L"ntdll.dll");
85 if (ntdll != NULL) {
86 ZwSetIntervalProfile = reinterpret_cast<ZwSetIntervalProfileFunc>(
87 ::GetProcAddress(ntdll, "ZwSetIntervalProfile"));
88 ZwQueryIntervalProfile = reinterpret_cast<ZwQueryIntervalProfileFunc>(
89 ::GetProcAddress(ntdll, "ZwQueryIntervalProfile"));
90 ZwCreateProfile = reinterpret_cast<ZwCreateProfileFunc>(
91 ::GetProcAddress(ntdll, "ZwCreateProfile"));
92 ZwStartProfile = reinterpret_cast<ZwStartProfileFunc>(
93 ::GetProcAddress(ntdll, "ZwStartProfile"));
94 ZwStopProfile = reinterpret_cast<ZwStopProfileFunc>(
95 ::GetProcAddress(ntdll, "ZwStopProfile"));
97 if (ZwSetIntervalProfile &&
98 ZwQueryIntervalProfile &&
99 ZwCreateProfile &&
100 ZwStartProfile &&
101 ZwStopProfile) {
102 initialized_ = true;
107 base::LazyInstance<ProfilerFuncs>::Leaky funcs = LAZY_INSTANCE_INITIALIZER;
109 } // namespace
112 namespace base {
113 namespace win {
115 SamplingProfiler::SamplingProfiler() : is_started_(false) {
118 SamplingProfiler::~SamplingProfiler() {
119 if (is_started_) {
120 CHECK(Stop()) <<
121 "Unable to stop sampling profiler, this will cause memory corruption.";
125 bool SamplingProfiler::Initialize(HANDLE process,
126 void* start,
127 size_t size,
128 size_t log2_bucket_size) {
129 // You only get to initialize each instance once.
130 DCHECK(!profile_handle_.IsValid());
131 DCHECK(!is_started_);
132 DCHECK(start != NULL);
133 DCHECK_NE(0U, size);
134 DCHECK_LE(2, log2_bucket_size);
135 DCHECK_GE(32, log2_bucket_size);
137 // Bail if the native functions weren't found.
138 if (!funcs.Get().initialized_)
139 return false;
141 size_t bucket_size = 1 << log2_bucket_size;
142 size_t num_buckets = (size + bucket_size - 1) / bucket_size;
143 DCHECK(num_buckets != 0);
144 buckets_.resize(num_buckets);
146 // Get our affinity mask for the call below.
147 DWORD_PTR process_affinity = 0;
148 DWORD_PTR system_affinity = 0;
149 if (!::GetProcessAffinityMask(process, &process_affinity, &system_affinity)) {
150 LOG(ERROR) << "Failed to get process affinity mask.";
151 return false;
154 HANDLE profile = NULL;
155 NTSTATUS status =
156 funcs.Get().ZwCreateProfile(&profile,
157 process,
158 start,
159 static_cast<ULONG>(size),
160 static_cast<ULONG>(log2_bucket_size),
161 &buckets_[0],
162 static_cast<ULONG>(
163 sizeof(buckets_[0]) * num_buckets),
164 ProfileTime,
165 process_affinity);
167 if (!NT_SUCCESS(status)) {
168 // Might as well deallocate the buckets.
169 buckets_.resize(0);
170 LOG(ERROR) << "Failed to create profile, error 0x" << std::hex << status;
171 return false;
174 DCHECK(profile != NULL);
175 profile_handle_.Set(profile);
177 return true;
180 bool SamplingProfiler::Start() {
181 DCHECK(profile_handle_.IsValid());
182 DCHECK(!is_started_);
183 DCHECK(funcs.Get().initialized_);
185 NTSTATUS status = funcs.Get().ZwStartProfile(profile_handle_.Get());
186 if (!NT_SUCCESS(status))
187 return false;
189 is_started_ = true;
191 return true;
194 bool SamplingProfiler::Stop() {
195 DCHECK(profile_handle_.IsValid());
196 DCHECK(is_started_);
197 DCHECK(funcs.Get().initialized_);
199 NTSTATUS status = funcs.Get().ZwStopProfile(profile_handle_.Get());
200 if (!NT_SUCCESS(status))
201 return false;
202 is_started_ = false;
204 return true;
207 bool SamplingProfiler::SetSamplingInterval(base::TimeDelta sampling_interval) {
208 if (!funcs.Get().initialized_)
209 return false;
211 // According to Nebbet, the sampling interval is in units of 100ns.
212 ULONG interval = sampling_interval.InMicroseconds() * 10;
213 NTSTATUS status = funcs.Get().ZwSetIntervalProfile(interval, ProfileTime);
214 if (!NT_SUCCESS(status))
215 return false;
217 return true;
220 bool SamplingProfiler::GetSamplingInterval(base::TimeDelta* sampling_interval) {
221 DCHECK(sampling_interval != NULL);
223 if (!funcs.Get().initialized_)
224 return false;
226 ULONG interval = 0;
227 NTSTATUS status = funcs.Get().ZwQueryIntervalProfile(ProfileTime, &interval);
228 if (!NT_SUCCESS(status))
229 return false;
231 // According to Nebbet, the sampling interval is in units of 100ns.
232 *sampling_interval = base::TimeDelta::FromMicroseconds(interval / 10);
234 return true;
237 } // namespace win
238 } // namespace base