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
{
15 ProfileAlignmentFixup
,
18 ProfileLoadInstructions
,
19 ProfilePipelineFrozen
,
20 ProfileBranchInstructions
,
21 ProfileTotalNonissues
,
25 ProfileBranchMispredictions
,
26 ProfileStoreInstructions
,
27 ProfileFpInstructions
,
28 ProfileIntegerInstructions
,
32 ProfileSpecialInstructions
,
35 ProfileDcacheAccesses
,
36 ProfileMemoryBarrierCycles
,
37 ProfileLoadLinkedIssues
,
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
,
52 ULONG eip_bucket_shift
,
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.
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.
77 ProfilerFuncs::ProfilerFuncs()
78 : ZwSetIntervalProfile(NULL
),
79 ZwQueryIntervalProfile(NULL
),
80 ZwCreateProfile(NULL
),
84 HMODULE ntdll
= ::GetModuleHandle(L
"ntdll.dll");
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
&&
107 base::LazyInstance
<ProfilerFuncs
>::Leaky funcs
= LAZY_INSTANCE_INITIALIZER
;
115 SamplingProfiler::SamplingProfiler() : is_started_(false) {
118 SamplingProfiler::~SamplingProfiler() {
121 "Unable to stop sampling profiler, this will cause memory corruption.";
125 bool SamplingProfiler::Initialize(HANDLE process
,
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
);
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_
)
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.";
154 HANDLE profile
= NULL
;
156 funcs
.Get().ZwCreateProfile(&profile
,
159 static_cast<ULONG
>(size
),
160 static_cast<ULONG
>(log2_bucket_size
),
163 sizeof(buckets_
[0]) * num_buckets
),
167 if (!NT_SUCCESS(status
)) {
168 // Might as well deallocate the buckets.
170 LOG(ERROR
) << "Failed to create profile, error 0x" << std::hex
<< status
;
174 DCHECK(profile
!= NULL
);
175 profile_handle_
.Set(profile
);
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
))
194 bool SamplingProfiler::Stop() {
195 DCHECK(profile_handle_
.IsValid());
197 DCHECK(funcs
.Get().initialized_
);
199 NTSTATUS status
= funcs
.Get().ZwStopProfile(profile_handle_
.Get());
200 if (!NT_SUCCESS(status
))
207 bool SamplingProfiler::SetSamplingInterval(base::TimeDelta sampling_interval
) {
208 if (!funcs
.Get().initialized_
)
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
))
220 bool SamplingProfiler::GetSamplingInterval(base::TimeDelta
* sampling_interval
) {
221 DCHECK(sampling_interval
!= NULL
);
223 if (!funcs
.Get().initialized_
)
227 NTSTATUS status
= funcs
.Get().ZwQueryIntervalProfile(ProfileTime
, &interval
);
228 if (!NT_SUCCESS(status
))
231 // According to Nebbet, the sampling interval is in units of 100ns.
232 *sampling_interval
= base::TimeDelta::FromMicroseconds(interval
/ 10);