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

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

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

Powered by Google App Engine
This is Rietveld 408576698