Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(32)

Side by Side Diff: base/win/sampling_profiler.cc

Issue 8803022: Windows-native sampling profiler wrapper class. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Implement & test Get|SetSamplingInterval. Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698