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

Side by Side Diff: base/profiler/stack_sampling_profiler_unittest.cc

Issue 1016563004: Statistical stack profiler for Windows x64 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@lkcr
Patch Set: Created 5 years, 9 months 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
OLDNEW
(Empty)
1 // Copyright 2015 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/bind.h"
6 #include "base/compiler_specific.h"
7 #include "base/path_service.h"
8 #include "base/profiler/stack_sampling_profiler.h"
9 #include "base/synchronization/waitable_event.h"
10 #include "base/threading/platform_thread.h"
11 #include "base/time/time.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13
14 namespace base {
15
16 using Module = StackSamplingProfiler::Module;
17 using Sample = StackSamplingProfiler::Sample;
18 using Profile = StackSamplingProfiler::Profile;
19
20 namespace {
21 // A thread to target for profiling, whose stack is guaranteed to contain
22 // SignalAndWaitUntilSignaled() when coordinated with the main thread.
23 class TargetThread : public PlatformThread::Delegate {
24 public:
25 TargetThread();
26
27 // Implementation of PlatformThread::Delegate:
28 void ThreadMain() override;
29
30 // Wait for the thread to have started and be executing in
31 // SignalAndWaitUntilSignaled().
32 void WaitForThreadStart();
33 // Allow the thread to return from SignalAndWaitUntilSignaled() and finish
34 // execution.
35 void SignalThreadToFinish();
36
37 // This function is guaranteed to be executing between calls to
38 // WaitForThreadStart() and SignalThreadToFinish().
39 static void SignalAndWaitUntilSignaled(WaitableEvent* thread_started_event,
40 WaitableEvent* finish_event);
41
42 PlatformThreadId id() const { return id_; }
43
44 private:
45 WaitableEvent thread_started_event_;
46 WaitableEvent finish_event_;
47 PlatformThreadId id_;
48
49 DISALLOW_COPY_AND_ASSIGN(TargetThread);
50 };
51
52 TargetThread::TargetThread()
53 : thread_started_event_(false, false), finish_event_(false, false),
54 id_(0) {}
55
56 void TargetThread::ThreadMain() {
57 id_ = PlatformThread::CurrentId();
58 SignalAndWaitUntilSignaled(&thread_started_event_, &finish_event_);
59 }
60
61 void TargetThread::WaitForThreadStart() {
62 thread_started_event_.Wait();
63 }
64
65 void TargetThread::SignalThreadToFinish() {
66 finish_event_.Signal();
67 }
68
69 // static
70 #if defined(_WIN64)
71 // Disable optimizations for this function so that it gets its own stack frame.
72 #pragma optimize("", off)
73 #endif
74 void TargetThread::SignalAndWaitUntilSignaled(
75 WaitableEvent* thread_started_event, WaitableEvent* finish_event) {
76 thread_started_event->Signal();
77 finish_event->Wait();
78 }
79 #if defined(_WIN64)
80 #pragma optimize("", on)
81 #endif
82
83 // Called on the profiler thread when complete. Collects profiles produced by
84 // the profiler, and signals an event to allow the main thread to know that that
85 // the profiler is done.
86 void SaveProfilesAndSignalEvent(
87 std::vector<Profile>* profiles, WaitableEvent* event,
88 const std::vector<Profile>& pending_profiles) {
89 *profiles = pending_profiles;
90 event->Signal();
91 }
92
93 // Captures profiles as specified by |params| on the TargetThread, and returns
94 // them in |profiles|. Waits up to |profiler_wait_time| for the profiler to
95 // complete.
96 void CaptureProfiles(const StackSamplingProfiler::SamplingParams& params,
97 std::vector<Profile>* profiles,
98 TimeDelta profiler_wait_time) {
99 TargetThread target_thread;
100 PlatformThreadHandle target_thread_handle;
101 EXPECT_TRUE(PlatformThread::Create(0, &target_thread, &target_thread_handle));
102
103 target_thread.WaitForThreadStart();
104
105 WaitableEvent sampling_thread_completed(true, false);
106 profiles->clear();
107 StackSamplingProfiler profiler(target_thread.id(), params);
108 profiler.SetCustomCompletedCallback(
109 Bind(&SaveProfilesAndSignalEvent, Unretained(profiles),
110 Unretained(&sampling_thread_completed)));
111 profiler.Start();
112 sampling_thread_completed.TimedWait(profiler_wait_time);
113 profiler.Stop();
114 sampling_thread_completed.Wait();
115
116 target_thread.SignalThreadToFinish();
117
118 PlatformThread::Join(target_thread_handle);
119 }
120
121 // If this executable was linked with /INCREMENTAL (the default for non-official
122 // debug and release builds on Windows), function addresses do not correspond to
123 // function code itself, but instead to instructions in the Incremental Link
124 // Table that jump to the functions. Check for a jump instruction and if present
125 // do a little decompilation to find the function's actual starting address.
126 const void* MaybeFixupFunctionAddressForILT(const void* function_address) {
127 #if defined(_WIN64)
128 const unsigned char *opcode =
129 reinterpret_cast<const unsigned char*>(function_address);
130 if (*opcode == 0xe9) {
131 // This is a relative jump instruction. Assume we're in the ILT and compute
132 // the function start address from the instruction offset.
133 const unsigned char* offset = opcode + 1;
134 const unsigned char* next_instruction = opcode + 5;
135 return next_instruction +
136 static_cast<int64>(*reinterpret_cast<const int32*>(offset));
137 }
138 #endif
139 return function_address;
140 }
141
142 // Searches through the frames in |sample|, returning an iterator to the first
143 // frame that has an instruction pointer between |function_address| and
144 // |function_address| + |size|. Returns sample.end() if no such frames are
145 // found.
146 Sample::const_iterator FindFirstFrameWithinFunction(
147 const Sample& sample, const void* function_address, int function_size) {
148 function_address = MaybeFixupFunctionAddressForILT(function_address);
149 for (auto it = sample.begin(); it != sample.end(); ++it) {
150 if ((reinterpret_cast<const unsigned char*>(it->instruction_pointer) >=
151 reinterpret_cast<const unsigned char*>(function_address)) &&
152 (reinterpret_cast<const unsigned char*>(it->instruction_pointer) <
153 (reinterpret_cast<const unsigned char*>(function_address) +
154 function_size)))
155 return it;
156 }
157 return sample.end();
158 }
159
160 // Returns a duration that is longer than the test timeout. We would use
161 // TimeDelta::Max() but https://crbug.com/465948.
162 TimeDelta AVeryLongTimeDelta() { return TimeDelta::FromDays(1); }
163 } // namespace
164
165
166 // The tests below are enabled for Win x64 only, pending implementation of the
167 // tested functionality on other platforms/architectures.
168
169 // Checks that the basic expected information is present in a sampled profile.
170 #if defined(_WIN64)
171 #define MAYBE_Basic Basic
172 #else
173 #define MAYBE_Basic DISABLED_Basic
174 #endif
175 TEST(StackSamplingProfilerTest, MAYBE_Basic) {
176 StackSamplingProfiler::SamplingParams params;
177 params.initial_delay = params.burst_interval = params.sampling_interval =
178 TimeDelta::FromMilliseconds(0);
179 params.bursts = 1;
180 params.samples_per_burst = 1;
181
182 std::vector<Profile> profiles;
183 CaptureProfiles(params, &profiles, AVeryLongTimeDelta());
184
185 // Check that the profile and samples sizes are correct, and the module
186 // indices are in range.
187
188 ASSERT_EQ(1u, profiles.size());
189 const Profile& profile = profiles[0];
190 ASSERT_EQ(1u, profile.samples.size());
191 EXPECT_EQ(params.sampling_interval, profile.sampling_period);
192 const Sample& sample = profile.samples[0];
193 for (const auto& frame : sample) {
194 ASSERT_GE(frame.module_index, 0);
195 ASSERT_LT(frame.module_index, static_cast<int>(profile.modules.size()));
196 }
197
198 // Check that the stack contains a frame for
199 // TargetThread::SignalAndWaitUntilSignaled() and that the frame has this
200 // executable's module.
201
202 // Since we don't have a good way to know the function size, use 100 bytes as
203 // a reasonable window to locate the instruction pointer.
204 Sample::const_iterator loc = FindFirstFrameWithinFunction(
205 sample,
206 reinterpret_cast<const void*>(&TargetThread::SignalAndWaitUntilSignaled),
207 100);
208 ASSERT_TRUE(loc != sample.end()) << "function not found in stack";
209
210 FilePath executable_path;
211 bool got_executable_path = PathService::Get(FILE_EXE, &executable_path);
212 EXPECT_TRUE(got_executable_path);
213 EXPECT_EQ(executable_path, profile.modules[loc->module_index].filename);
214 }
215
216 // Checks that the expected number of profiles and samples are present in the
217 // profiles produced.
218 #if defined(_WIN64)
219 #define MAYBE_MultipleProfilesAndSamples MultipleProfilesAndSamples
220 #else
221 #define MAYBE_MultipleProfilesAndSamples DISABLED_MultipleProfilesAndSamples
222 #endif
223 TEST(StackSamplingProfilerTest, MAYBE_MultipleProfilesAndSamples) {
224 StackSamplingProfiler::SamplingParams params;
225 params.initial_delay = params.burst_interval = params.sampling_interval =
226 TimeDelta::FromMilliseconds(0);
227 params.bursts = 2;
228 params.samples_per_burst = 3;
229
230 std::vector<Profile> profiles;
231 CaptureProfiles(params, &profiles, AVeryLongTimeDelta());
232
233 ASSERT_EQ(2u, profiles.size());
234 EXPECT_EQ(3u, profiles[0].samples.size());
235 EXPECT_EQ(3u, profiles[1].samples.size());
236 }
237
238 // Checks that no profiles are captured if the profiling is stopped during the
239 // initial delay.
240 #if defined(_WIN64)
241 #define MAYBE_StopDuringInitialDelay StopDuringInitialDelay
242 #else
243 #define MAYBE_StopDuringInitialDelay DISABLED_StopDuringInitialDelay
244 #endif
245 TEST(StackSamplingProfilerTest, MAYBE_StopDuringInitialDelay) {
246 StackSamplingProfiler::SamplingParams params;
247 params.burst_interval = params.sampling_interval =
248 TimeDelta::FromMilliseconds(0);
249 params.initial_delay = TimeDelta::FromSeconds(60);
250 params.bursts = params.samples_per_burst = 1;
251
252 std::vector<Profile> profiles;
253 CaptureProfiles(params, &profiles, TimeDelta::FromMilliseconds(0));
254
255 EXPECT_TRUE(profiles.empty());
256 }
257
258 // Checks that the single completed profile is captured if the profiling is
259 // stopped between bursts.
260 #if defined(_WIN64)
261 #define MAYBE_StopDuringInterBurstInterval StopDuringInterBurstInterval
262 #else
263 #define MAYBE_StopDuringInterBurstInterval DISABLED_StopDuringInterBurstInterval
264 #endif
265 TEST(StackSamplingProfilerTest, MAYBE_StopDuringInterBurstInterval) {
266 StackSamplingProfiler::SamplingParams params;
267 params.initial_delay = params.sampling_interval =
268 TimeDelta::FromMilliseconds(0);
269 params.burst_interval = TimeDelta::FromSeconds(60);
270 params.bursts = 2;
271 params.samples_per_burst = 1;
272
273 std::vector<Profile> profiles;
274 CaptureProfiles(params, &profiles, TimeDelta::FromMilliseconds(50));
275
276 ASSERT_EQ(1u, profiles.size());
277 EXPECT_EQ(1u, profiles[0].samples.size());
278 }
279
280 // Checks that only completed profiles are captured.
281 #if defined(_WIN64)
282 #define MAYBE_StopDuringInterSampleInterval StopDuringInterSampleInterval
283 #else
284 #define MAYBE_StopDuringInterSampleInterval DISABLED_StopDuringInterSampleInterv al
285 #endif
286 TEST(StackSamplingProfilerTest, MAYBE_StopDuringInterSampleInterval) {
287 StackSamplingProfiler::SamplingParams params;
288 params.initial_delay = params.burst_interval =
289 TimeDelta::FromMilliseconds(0);
290 params.sampling_interval = TimeDelta::FromSeconds(60);
291 params.bursts = 1;
292 params.samples_per_burst = 2;
293
294 std::vector<Profile> profiles;
295 CaptureProfiles(params, &profiles, TimeDelta::FromMilliseconds(50));
296
297 EXPECT_TRUE(profiles.empty());
298 }
299
300 } // namespace tracked_objects
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698