OLD | NEW |
| (Empty) |
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. | |
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() | |
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")); | |
96 | |
97 if (ZwSetIntervalProfile && | |
98 ZwQueryIntervalProfile && | |
99 ZwCreateProfile && | |
100 ZwStartProfile && | |
101 ZwStopProfile) { | |
102 initialized_ = true; | |
103 } | |
104 } | |
105 } | |
106 | |
107 base::LazyInstance<ProfilerFuncs>::Leaky funcs = LAZY_INSTANCE_INITIALIZER; | |
108 | |
109 } // namespace | |
110 | |
111 | |
112 namespace base { | |
113 namespace win { | |
114 | |
115 SamplingProfiler::SamplingProfiler() : is_started_(false) { | |
116 } | |
117 | |
118 SamplingProfiler::~SamplingProfiler() { | |
119 if (is_started_) { | |
120 CHECK(Stop()) << | |
121 "Unable to stop sampling profiler, this will cause memory corruption."; | |
122 } | |
123 } | |
124 | |
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); | |
136 | |
137 // Bail if the native functions weren't found. | |
138 if (!funcs.Get().initialized_) | |
139 return false; | |
140 | |
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); | |
145 | |
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; | |
152 } | |
153 | |
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); | |
166 | |
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; | |
172 } | |
173 | |
174 DCHECK(profile != NULL); | |
175 profile_handle_.Set(profile); | |
176 | |
177 return true; | |
178 } | |
179 | |
180 bool SamplingProfiler::Start() { | |
181 DCHECK(profile_handle_.IsValid()); | |
182 DCHECK(!is_started_); | |
183 DCHECK(funcs.Get().initialized_); | |
184 | |
185 NTSTATUS status = funcs.Get().ZwStartProfile(profile_handle_.Get()); | |
186 if (!NT_SUCCESS(status)) | |
187 return false; | |
188 | |
189 is_started_ = true; | |
190 | |
191 return true; | |
192 } | |
193 | |
194 bool SamplingProfiler::Stop() { | |
195 DCHECK(profile_handle_.IsValid()); | |
196 DCHECK(is_started_); | |
197 DCHECK(funcs.Get().initialized_); | |
198 | |
199 NTSTATUS status = funcs.Get().ZwStopProfile(profile_handle_.Get()); | |
200 if (!NT_SUCCESS(status)) | |
201 return false; | |
202 is_started_ = false; | |
203 | |
204 return true; | |
205 } | |
206 | |
207 bool SamplingProfiler::SetSamplingInterval(base::TimeDelta sampling_interval) { | |
208 if (!funcs.Get().initialized_) | |
209 return false; | |
210 | |
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; | |
216 | |
217 return true; | |
218 } | |
219 | |
220 bool SamplingProfiler::GetSamplingInterval(base::TimeDelta* sampling_interval) { | |
221 DCHECK(sampling_interval != NULL); | |
222 | |
223 if (!funcs.Get().initialized_) | |
224 return false; | |
225 | |
226 ULONG interval = 0; | |
227 NTSTATUS status = funcs.Get().ZwQueryIntervalProfile(ProfileTime, &interval); | |
228 if (!NT_SUCCESS(status)) | |
229 return false; | |
230 | |
231 // According to Nebbet, the sampling interval is in units of 100ns. | |
232 *sampling_interval = base::TimeDelta::FromMicroseconds(interval / 10); | |
233 | |
234 return true; | |
235 } | |
236 | |
237 } // namespace win | |
238 } // namespace base | |
OLD | NEW |