OLD | NEW |
---|---|
(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, | |
76 WaitableEvent* finish_event) { | |
77 thread_started_event->Signal(); | |
78 finish_event->Wait(); | |
79 } | |
80 #if defined(_WIN64) | |
81 #pragma optimize("", on) | |
82 #endif | |
83 | |
84 // Called on the profiler thread when complete. Collects profiles produced by | |
85 // the profiler, and signals an event to allow the main thread to know that that | |
86 // the profiler is done. | |
87 void SaveProfilesAndSignalEvent(std::vector<Profile>* profiles, | |
88 WaitableEvent* event, | |
89 const std::vector<Profile>& pending_profiles) { | |
90 *profiles = pending_profiles; | |
91 event->Signal(); | |
92 } | |
93 | |
94 // Captures profiles as specified by |params| on the TargetThread, and returns | |
95 // them in |profiles|. Waits up to |profiler_wait_time| for the profiler to | |
96 // complete. | |
97 void CaptureProfiles(const StackSamplingProfiler::SamplingParams& params, | |
98 std::vector<Profile>* profiles, | |
99 TimeDelta profiler_wait_time) { | |
100 TargetThread target_thread; | |
101 PlatformThreadHandle target_thread_handle; | |
102 EXPECT_TRUE(PlatformThread::Create(0, &target_thread, &target_thread_handle)); | |
103 | |
104 target_thread.WaitForThreadStart(); | |
105 | |
106 WaitableEvent sampling_thread_completed(true, false); | |
107 profiles->clear(); | |
108 StackSamplingProfiler profiler(target_thread.id(), params); | |
109 profiler.SetCustomCompletedCallback( | |
110 Bind(&SaveProfilesAndSignalEvent, Unretained(profiles), | |
111 Unretained(&sampling_thread_completed))); | |
112 profiler.Start(); | |
113 sampling_thread_completed.TimedWait(profiler_wait_time); | |
114 profiler.Stop(); | |
115 sampling_thread_completed.Wait(); | |
116 | |
117 target_thread.SignalThreadToFinish(); | |
118 | |
119 PlatformThread::Join(target_thread_handle); | |
120 } | |
121 | |
122 // If this executable was linked with /INCREMENTAL (the default for non-official | |
123 // debug and release builds on Windows), function addresses do not correspond to | |
124 // function code itself, but instead to instructions in the Incremental Link | |
125 // Table that jump to the functions. Check for a jump instruction and if present | |
126 // do a little decompilation to find the function's actual starting address. | |
127 const void* MaybeFixupFunctionAddressForILT(const void* function_address) { | |
128 #if defined(_WIN64) | |
129 const unsigned char* opcode = | |
130 reinterpret_cast<const unsigned char*>(function_address); | |
131 if (*opcode == 0xe9) { | |
132 // This is a relative jump instruction. Assume we're in the ILT and compute | |
133 // the function start address from the instruction offset. | |
134 const unsigned char* offset = opcode + 1; | |
135 const unsigned char* next_instruction = opcode + 5; | |
136 return next_instruction + | |
137 static_cast<int64>(*reinterpret_cast<const int32*>(offset)); | |
138 } | |
139 #endif | |
140 return function_address; | |
141 } | |
142 | |
143 // Searches through the frames in |sample|, returning an iterator to the first | |
144 // frame that has an instruction pointer between |function_address| and | |
145 // |function_address| + |size|. Returns sample.end() if no such frames are | |
146 // found. | |
147 Sample::const_iterator FindFirstFrameWithinFunction( | |
148 const Sample& sample, | |
149 const void* function_address, | |
150 int function_size) { | |
151 function_address = MaybeFixupFunctionAddressForILT(function_address); | |
152 for (auto it = sample.begin(); it != sample.end(); ++it) { | |
153 if ((reinterpret_cast<const unsigned char*>(it->instruction_pointer) >= | |
154 reinterpret_cast<const unsigned char*>(function_address)) && | |
155 (reinterpret_cast<const unsigned char*>(it->instruction_pointer) < | |
156 (reinterpret_cast<const unsigned char*>(function_address) + | |
157 function_size))) | |
158 return it; | |
159 } | |
160 return sample.end(); | |
161 } | |
162 | |
163 // Returns a duration that is longer than the test timeout. We would use | |
164 // TimeDelta::Max() but https://crbug.com/465948. | |
165 TimeDelta AVeryLongTimeDelta() { return TimeDelta::FromDays(1); } | |
166 } // namespace | |
167 | |
168 | |
169 // The tests below are enabled for Win x64 only, pending implementation of the | |
170 // tested functionality on other platforms/architectures. | |
171 | |
172 // Checks that the basic expected information is present in a sampled profile. | |
173 #if defined(_WIN64) | |
174 #define MAYBE_Basic Basic | |
175 #else | |
176 #define MAYBE_Basic DISABLED_Basic | |
177 #endif | |
178 TEST(StackSamplingProfilerTest, MAYBE_Basic) { | |
179 StackSamplingProfiler::SamplingParams params; | |
180 params.initial_delay = params.burst_interval = params.sampling_interval = | |
181 TimeDelta::FromMilliseconds(0); | |
182 params.bursts = 1; | |
183 params.samples_per_burst = 1; | |
184 | |
185 std::vector<Profile> profiles; | |
186 CaptureProfiles(params, &profiles, AVeryLongTimeDelta()); | |
187 | |
188 // Check that the profile and samples sizes are correct, and the module | |
189 // indices are in range. | |
190 | |
191 ASSERT_EQ(1u, profiles.size()); | |
192 const Profile& profile = profiles[0]; | |
193 ASSERT_EQ(1u, profile.samples.size()); | |
194 EXPECT_EQ(params.sampling_interval, profile.sampling_period); | |
195 const Sample& sample = profile.samples[0]; | |
196 for (const auto& frame : sample) { | |
197 ASSERT_GE(frame.module_index, 0); | |
198 ASSERT_LT(frame.module_index, static_cast<int>(profile.modules.size())); | |
199 } | |
200 | |
201 // Check that the stack contains a frame for | |
202 // TargetThread::SignalAndWaitUntilSignaled() and that the frame has this | |
203 // executable's module. | |
204 | |
205 // Since we don't have a good way to know the function size, use 100 bytes as | |
206 // a reasonable window to locate the instruction pointer. | |
207 Sample::const_iterator loc = FindFirstFrameWithinFunction( | |
208 sample, | |
209 reinterpret_cast<const void*>(&TargetThread::SignalAndWaitUntilSignaled), | |
210 100); | |
211 ASSERT_TRUE(loc != sample.end()) << "function not found in stack"; | |
cpu_(ooo_6.6-7.5)
2015/03/20 19:34:41
in the failure case, lets dump what we got to stdo
Mike Wittman
2015/03/20 20:56:20
Done.
| |
212 | |
213 FilePath executable_path; | |
214 bool got_executable_path = PathService::Get(FILE_EXE, &executable_path); | |
215 EXPECT_TRUE(got_executable_path); | |
216 EXPECT_EQ(executable_path, profile.modules[loc->module_index].filename); | |
217 } | |
218 | |
219 // Checks that the expected number of profiles and samples are present in the | |
220 // profiles produced. | |
221 #if defined(_WIN64) | |
222 #define MAYBE_MultipleProfilesAndSamples MultipleProfilesAndSamples | |
223 #else | |
224 #define MAYBE_MultipleProfilesAndSamples DISABLED_MultipleProfilesAndSamples | |
225 #endif | |
226 TEST(StackSamplingProfilerTest, MAYBE_MultipleProfilesAndSamples) { | |
227 StackSamplingProfiler::SamplingParams params; | |
228 params.initial_delay = params.burst_interval = params.sampling_interval = | |
229 TimeDelta::FromMilliseconds(0); | |
230 params.bursts = 2; | |
231 params.samples_per_burst = 3; | |
232 | |
233 std::vector<Profile> profiles; | |
234 CaptureProfiles(params, &profiles, AVeryLongTimeDelta()); | |
235 | |
236 ASSERT_EQ(2u, profiles.size()); | |
237 EXPECT_EQ(3u, profiles[0].samples.size()); | |
238 EXPECT_EQ(3u, profiles[1].samples.size()); | |
239 } | |
240 | |
241 // Checks that no profiles are captured if the profiling is stopped during the | |
242 // initial delay. | |
243 #if defined(_WIN64) | |
244 #define MAYBE_StopDuringInitialDelay StopDuringInitialDelay | |
245 #else | |
246 #define MAYBE_StopDuringInitialDelay DISABLED_StopDuringInitialDelay | |
247 #endif | |
248 TEST(StackSamplingProfilerTest, MAYBE_StopDuringInitialDelay) { | |
249 StackSamplingProfiler::SamplingParams params; | |
250 params.burst_interval = params.sampling_interval = | |
251 TimeDelta::FromMilliseconds(0); | |
252 params.initial_delay = TimeDelta::FromSeconds(60); | |
253 params.bursts = params.samples_per_burst = 1; | |
254 | |
255 std::vector<Profile> profiles; | |
256 CaptureProfiles(params, &profiles, TimeDelta::FromMilliseconds(0)); | |
257 | |
258 EXPECT_TRUE(profiles.empty()); | |
259 } | |
260 | |
261 // Checks that the single completed profile is captured if the profiling is | |
262 // stopped between bursts. | |
263 #if defined(_WIN64) | |
264 #define MAYBE_StopDuringInterBurstInterval StopDuringInterBurstInterval | |
265 #else | |
266 #define MAYBE_StopDuringInterBurstInterval DISABLED_StopDuringInterBurstInterval | |
267 #endif | |
268 TEST(StackSamplingProfilerTest, MAYBE_StopDuringInterBurstInterval) { | |
269 StackSamplingProfiler::SamplingParams params; | |
270 params.initial_delay = params.sampling_interval = | |
271 TimeDelta::FromMilliseconds(0); | |
272 params.burst_interval = TimeDelta::FromSeconds(60); | |
273 params.bursts = 2; | |
274 params.samples_per_burst = 1; | |
275 | |
276 std::vector<Profile> profiles; | |
277 CaptureProfiles(params, &profiles, TimeDelta::FromMilliseconds(50)); | |
278 | |
279 ASSERT_EQ(1u, profiles.size()); | |
280 EXPECT_EQ(1u, profiles[0].samples.size()); | |
281 } | |
282 | |
283 // Checks that only completed profiles are captured. | |
284 #if defined(_WIN64) | |
285 #define MAYBE_StopDuringInterSampleInterval StopDuringInterSampleInterval | |
286 #else | |
287 #define MAYBE_StopDuringInterSampleInterval \ | |
288 DISABLED_StopDuringInterSampleInterval | |
289 #endif | |
290 TEST(StackSamplingProfilerTest, MAYBE_StopDuringInterSampleInterval) { | |
291 StackSamplingProfiler::SamplingParams params; | |
292 params.initial_delay = params.burst_interval = TimeDelta::FromMilliseconds(0); | |
293 params.sampling_interval = TimeDelta::FromSeconds(60); | |
294 params.bursts = 1; | |
295 params.samples_per_burst = 2; | |
296 | |
297 std::vector<Profile> profiles; | |
298 CaptureProfiles(params, &profiles, TimeDelta::FromMilliseconds(50)); | |
299 | |
300 EXPECT_TRUE(profiles.empty()); | |
301 } | |
302 | |
303 } // namespace tracked_objects | |
OLD | NEW |