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 "chrome/browser/metrics/call_stack_profile_metrics_provider.h" | |
6 | |
7 #include "base/memory/scoped_ptr.h" | |
8 #include "base/profiler/stack_sampling_profiler.h" | |
9 #include "base/strings/string_number_conversions.h" | |
10 #include "components/metrics/proto/chrome_user_metrics_extension.pb.h" | |
11 #include "testing/gtest/include/gtest/gtest.h" | |
12 | |
13 using base::StackSamplingProfiler; | |
14 using Frame = StackSamplingProfiler::Frame; | |
15 using Module = StackSamplingProfiler::Module; | |
16 using Profile = StackSamplingProfiler::Profile; | |
17 using Sample = StackSamplingProfiler::Sample; | |
Ilya Sherman
2015/03/17 04:54:35
nit: Why the different syntax on lines 14-17 vs. l
Mike Wittman
2015/03/18 00:54:27
"using X::Y" is only valid if X is a namespace.
| |
18 | |
19 // Checks that all properties from multiple profiles are filled as expected. | |
20 TEST(CallStackProfileMetricsProviderTest, MultipleProfiles) { | |
21 const uintptr_t module1_base_address = 0x1000; | |
22 const uintptr_t module2_base_address = 0x2000; | |
23 const uintptr_t module3_base_address = 0x3000; | |
24 | |
25 const Module profile_modules[][2] = { | |
26 { | |
27 { | |
28 reinterpret_cast<const void*>(module1_base_address), | |
29 "ABCD", | |
30 #if defined(OS_WIN) | |
Alexei Svitkine (slow)
2015/03/17 23:10:10
Do the actual paths matter?
If not, I suggest sim
Mike Wittman
2015/03/18 01:48:36
Not that much. However, even if we use FILE_PATH_L
| |
31 base::FilePath(L"c:\\some\\path\\to\\chrome.exe") | |
32 #else | |
33 base::FilePath("/some/path/to/chrome") | |
34 #endif | |
35 }, | |
36 { | |
37 reinterpret_cast<const void*>(module2_base_address), | |
38 "EFGH", | |
39 #if defined(OS_WIN) | |
40 base::FilePath(L"c:\\some\\path\\to\\third_party.dll") | |
41 #else | |
42 base::FilePath("/some/path/to/third_party.so") | |
43 #endif | |
44 } | |
45 }, | |
46 { | |
47 { | |
48 reinterpret_cast<const void*>(module3_base_address), | |
49 "MNOP", | |
50 #if defined(OS_WIN) | |
51 base::FilePath(L"c:\\some\\path\\to\\third_party2.dll") | |
52 #else | |
53 base::FilePath("/some/path/to/third_party2.so") | |
54 #endif | |
55 }, | |
56 { // Repeated from the first profile. | |
57 reinterpret_cast<const void*>(module1_base_address), | |
58 "ABCD", | |
59 #if defined(OS_WIN) | |
60 base::FilePath(L"c:\\some\\path\\to\\chrome.exe") | |
61 #else | |
62 base::FilePath("/some/path/to/chrome") | |
63 #endif | |
64 }, | |
65 } | |
66 }; | |
67 | |
68 // Values for Windows generated with: | |
69 // perl -MDigest::MD5=md5 -MEncode=encode \ | |
70 // -e 'for(@ARGV){printf "%x\n", unpack "Q", md5 encode "UTF-16LE", $_}' \ | |
71 // chrome.exe third_party.dll third_party2.dll | |
72 // | |
73 // Values for Linux generated with: | |
74 // perl -MDigest::MD5=md5 \ | |
75 // -e 'for(@ARGV){printf "%x\n", unpack "Q", md5 $_}' \ | |
76 // chrome third_party.so third_party2.so | |
77 const uint64 profile_expected_name_md5_prefixes[][2] = { | |
78 { | |
79 #if defined(OS_WIN) | |
80 0x2ac596616e4c346ULL, | |
81 0xba1aaedefd8b2b7eULL | |
82 #else | |
83 0x6cc31a45a8384855UL, | |
84 0xf8c9598614613684UL | |
85 #endif | |
86 }, | |
87 { | |
88 #if defined(OS_WIN) | |
89 0xcad5a473456fb687ULL, | |
90 0x2ac596616e4c346ULL | |
91 #else | |
92 0x9eeca69f537e64b4UL, | |
93 0x6cc31a45a8384855UL | |
94 #endif | |
95 } | |
96 }; | |
97 | |
98 const Frame profile_sample_frames[][2][3] = { | |
Ilya Sherman
2015/03/17 04:54:35
It's not immediately clear to me what this variabl
Mike Wittman
2015/03/18 00:54:27
Done.
| |
99 { | |
100 { | |
101 { reinterpret_cast<const void*>(module1_base_address + 0x10), 0 }, | |
102 { reinterpret_cast<const void*>(module2_base_address + 0x20), 1 }, | |
103 { reinterpret_cast<const void*>(module1_base_address + 0x30), 0 } | |
104 }, | |
105 { | |
106 { reinterpret_cast<const void*>(module2_base_address + 0x10), 1 }, | |
107 { reinterpret_cast<const void*>(module1_base_address + 0x20), 0 }, | |
108 { reinterpret_cast<const void*>(module2_base_address + 0x30), 1 } | |
109 } | |
110 }, | |
111 { | |
112 { | |
113 { reinterpret_cast<const void*>(module3_base_address + 0x10), 0 }, | |
114 { reinterpret_cast<const void*>(module1_base_address + 0x20), 1 }, | |
115 { reinterpret_cast<const void*>(module3_base_address + 0x30), 0 } | |
116 }, | |
117 { | |
118 { reinterpret_cast<const void*>(module1_base_address + 0x10), 1 }, | |
119 { reinterpret_cast<const void*>(module3_base_address + 0x20), 0 }, | |
120 { reinterpret_cast<const void*>(module1_base_address + 0x30), 1 } | |
121 } | |
122 } | |
123 }; | |
124 | |
125 base::TimeDelta profile_durations[2] = { | |
126 base::TimeDelta::FromMilliseconds(100), | |
127 base::TimeDelta::FromMilliseconds(200) | |
128 }; | |
129 | |
130 base::TimeDelta profile_sampling_periods[2] = { | |
131 base::TimeDelta::FromMilliseconds(10), | |
132 base::TimeDelta::FromMilliseconds(20) | |
133 }; | |
134 | |
135 std::vector<Profile> profiles; | |
136 for (size_t i = 0; i < arraysize(profile_sample_frames); i++) { | |
Ilya Sherman
2015/03/17 04:54:35
nit: ++i, ++j below
Mike Wittman
2015/03/18 00:54:27
Done.
| |
137 Profile profile; | |
138 profile.modules.insert( | |
139 profile.modules.end(), &profile_modules[i][0], | |
140 &profile_modules[i][0] + arraysize(profile_modules[i])); | |
141 | |
142 for (size_t j = 0; j < arraysize(profile_sample_frames[i]); j++) { | |
143 profile.samples.push_back(Sample()); | |
144 Sample& sample = profile.samples.back(); | |
145 sample.insert(sample.end(), &profile_sample_frames[i][j][0], | |
146 &profile_sample_frames[i][j][0] + | |
147 arraysize(profile_sample_frames[i][j])); | |
148 } | |
149 | |
150 profile.profile_duration = profile_durations[i]; | |
151 profile.sampling_period = profile_sampling_periods[i]; | |
152 profile.preserve_sample_ordering = false; | |
153 | |
154 profiles.push_back(profile); | |
155 } | |
156 | |
157 scoped_ptr<CallStackProfileMetricsProvider> provider( | |
158 new CallStackProfileMetricsProvider); | |
Ilya Sherman
2015/03/17 04:54:35
nit: Why not stack-allocate?
Mike Wittman
2015/03/18 00:54:27
Stack allocation works.
| |
159 provider->SetSourceProfilesForTest(profiles); | |
160 metrics::ChromeUserMetricsExtension uma_proto; | |
161 provider->ProvideGeneralMetrics(&uma_proto); | |
162 | |
163 ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames)), | |
164 uma_proto.sampled_profile().size()); | |
165 for (size_t i = 0; i < arraysize(profile_sample_frames); i++) { | |
166 SCOPED_TRACE("profile " + base::IntToString(i)); | |
167 const metrics::SampledProfile& sampled_profile = | |
168 uma_proto.sampled_profile().Get(i); | |
169 ASSERT_TRUE(sampled_profile.has_call_stack_profile()); | |
170 const metrics::CallStackProfile& call_stack_profile = | |
171 sampled_profile.call_stack_profile(); | |
172 | |
173 ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames[i])), | |
174 call_stack_profile.sample().size()); | |
175 for (size_t j = 0; j < arraysize(profile_sample_frames[i]); j++) { | |
176 SCOPED_TRACE("sample " + base::IntToString(j)); | |
177 const metrics::CallStackProfile::Sample& proto_sample = | |
178 call_stack_profile.sample().Get(j); | |
179 ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames[i][j])), | |
180 proto_sample.entry().size()); | |
181 ASSERT_TRUE(proto_sample.has_count()); | |
182 EXPECT_EQ(1u, proto_sample.count()); | |
183 for (size_t k = 0; k < arraysize(profile_sample_frames[i][j]); k++) { | |
184 SCOPED_TRACE("frame " + base::IntToString(k)); | |
185 const metrics::CallStackEntry& entry = proto_sample.entry().Get(k); | |
186 ASSERT_TRUE(entry.has_address()); | |
187 const char *instruction_pointer = reinterpret_cast<const char *>( | |
Ilya Sherman
2015/03/17 04:54:35
nit: "char *" -> "char* "
Mike Wittman
2015/03/18 00:54:27
Done.
| |
188 profile_sample_frames[i][j][k].instruction_pointer); | |
189 const char *module_base_address = reinterpret_cast<const char *>( | |
190 profile_modules[i][profile_sample_frames[i][j][k].module_index] | |
191 .base_address); | |
192 EXPECT_EQ(static_cast<uint64>(instruction_pointer - | |
193 module_base_address), entry.address()); | |
194 ASSERT_TRUE(entry.has_module_id_index()); | |
195 EXPECT_EQ(profile_sample_frames[i][j][k].module_index, | |
196 entry.module_id_index()); | |
197 } | |
198 } | |
199 | |
200 ASSERT_EQ(static_cast<int>(arraysize(profile_modules[i])), | |
201 call_stack_profile.module_id().size()); | |
202 for (size_t j = 0; j < arraysize(profile_modules[i]); j++) { | |
203 SCOPED_TRACE("module " + base::IntToString(j)); | |
204 const metrics::ModuleIdentifier& module_identifier = | |
205 call_stack_profile.module_id().Get(j); | |
206 ASSERT_TRUE(module_identifier.has_build_id()); | |
207 EXPECT_EQ(profile_modules[i][j].id, module_identifier.build_id()); | |
208 ASSERT_TRUE(module_identifier.has_name_md5_prefix()); | |
209 EXPECT_EQ(profile_expected_name_md5_prefixes[i][j], | |
210 module_identifier.name_md5_prefix()); | |
211 } | |
212 | |
213 ASSERT_TRUE(call_stack_profile.has_profile_duration_ms()); | |
214 EXPECT_EQ(profile_durations[i].InMilliseconds(), | |
215 call_stack_profile.profile_duration_ms()); | |
216 ASSERT_TRUE(call_stack_profile.has_sampling_period_ms()); | |
217 EXPECT_EQ(profile_sampling_periods[i].InMilliseconds(), | |
218 call_stack_profile.sampling_period_ms()); | |
219 } | |
220 } | |
Ilya Sherman
2015/03/17 04:54:35
This test is super long and has a lot of nested lo
Mike Wittman
2015/03/18 00:54:27
It is very long. But it also tests that there's no
| |
221 | |
222 // Checks that all duplicate samples are collapsed with | |
223 // preserve_sample_ordering = false. | |
224 TEST(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) { | |
225 const uintptr_t module_base_address = 0x1000; | |
226 | |
227 const Module modules[] = { | |
228 { | |
229 reinterpret_cast<const void*>(module_base_address), | |
230 "ABCD", | |
231 #if defined(OS_WIN) | |
232 base::FilePath(L"c:\\some\\path\\to\\chrome.exe") | |
233 #else | |
234 base::FilePath("/some/path/to/chrome") | |
235 #endif | |
236 } | |
237 }; | |
238 | |
239 // Duplicate samples in slots 0, 2, and 3. | |
240 const Frame sample_frames[][1] = { | |
241 { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, }, | |
242 { { reinterpret_cast<const void*>(module_base_address + 0x20), 0 }, }, | |
243 { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, }, | |
244 { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, }, | |
245 }; | |
246 | |
247 Profile profile; | |
248 profile.modules.insert(profile.modules.end(), &modules[0], | |
249 &modules[0] + arraysize(modules)); | |
250 | |
251 for (size_t i = 0; i < arraysize(sample_frames); i++) { | |
252 profile.samples.push_back(Sample()); | |
253 Sample& sample = profile.samples.back(); | |
254 sample.insert(sample.end(), &sample_frames[i][0], | |
255 &sample_frames[i][0] + arraysize(sample_frames[i])); | |
256 } | |
257 | |
258 profile.profile_duration = base::TimeDelta::FromMilliseconds(100); | |
259 profile.sampling_period = base::TimeDelta::FromMilliseconds(10); | |
260 profile.preserve_sample_ordering = false; | |
261 | |
262 scoped_ptr<CallStackProfileMetricsProvider> provider( | |
263 new CallStackProfileMetricsProvider); | |
264 provider->SetSourceProfilesForTest(std::vector<Profile>(1, profile)); | |
265 metrics::ChromeUserMetricsExtension uma_proto; | |
266 provider->ProvideGeneralMetrics(&uma_proto); | |
267 | |
268 ASSERT_EQ(1, uma_proto.sampled_profile().size()); | |
269 const metrics::SampledProfile& sampled_profile = | |
270 uma_proto.sampled_profile().Get(0); | |
271 ASSERT_TRUE(sampled_profile.has_call_stack_profile()); | |
272 const metrics::CallStackProfile& call_stack_profile = | |
273 sampled_profile.call_stack_profile(); | |
274 | |
275 ASSERT_EQ(2, call_stack_profile.sample().size()); | |
276 for (int i = 0; i < 2; i++) { | |
277 SCOPED_TRACE("sample " + base::IntToString(i)); | |
278 const metrics::CallStackProfile::Sample& proto_sample = | |
279 call_stack_profile.sample().Get(i); | |
280 ASSERT_EQ(static_cast<int>(arraysize(sample_frames[i])), | |
281 proto_sample.entry().size()); | |
282 ASSERT_TRUE(proto_sample.has_count()); | |
283 EXPECT_EQ(i == 0 ? 3u : 1u, proto_sample.count()); | |
284 for (size_t j = 0; j < arraysize(sample_frames[i]); j++) { | |
285 SCOPED_TRACE("frame " + base::IntToString(j)); | |
286 const metrics::CallStackEntry& entry = proto_sample.entry().Get(j); | |
287 ASSERT_TRUE(entry.has_address()); | |
288 const char *instruction_pointer = reinterpret_cast<const char *>( | |
289 sample_frames[i][j].instruction_pointer); | |
290 const char *module_base_address = reinterpret_cast<const char *>( | |
291 modules[sample_frames[i][j].module_index].base_address); | |
292 EXPECT_EQ(static_cast<uint64>(instruction_pointer - module_base_address), | |
293 entry.address()); | |
294 ASSERT_TRUE(entry.has_module_id_index()); | |
295 EXPECT_EQ(sample_frames[i][j].module_index, entry.module_id_index()); | |
296 } | |
297 } | |
298 } | |
299 | |
300 // Checks that only contiguous duplicate samples are collapsed with | |
301 // preserve_sample_ordering = true. | |
302 TEST(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) { | |
303 const uintptr_t module_base_address = 0x1000; | |
304 | |
305 const Module modules[] = { | |
306 { | |
307 reinterpret_cast<const void*>(module_base_address), | |
308 "ABCD", | |
309 #if defined(OS_WIN) | |
310 base::FilePath(L"c:\\some\\path\\to\\chrome.exe") | |
311 #else | |
312 base::FilePath("/some/path/to/chrome") | |
313 #endif | |
314 } | |
315 }; | |
316 | |
317 // Duplicate samples in slots 0, 2, and 3. | |
318 const Frame sample_frames[][1] = { | |
319 { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, }, | |
320 { { reinterpret_cast<const void*>(module_base_address + 0x20), 0 }, }, | |
321 { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, }, | |
322 { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, } | |
323 }; | |
324 | |
325 Profile profile; | |
326 profile.modules.insert(profile.modules.end(), &modules[0], | |
327 &modules[0] + arraysize(modules)); | |
328 | |
329 for (size_t i = 0; i < arraysize(sample_frames); i++) { | |
330 profile.samples.push_back(Sample()); | |
331 Sample& sample = profile.samples.back(); | |
332 sample.insert(sample.end(), &sample_frames[i][0], | |
333 &sample_frames[i][0] + arraysize(sample_frames[i])); | |
334 } | |
335 | |
336 profile.profile_duration = base::TimeDelta::FromMilliseconds(100); | |
337 profile.sampling_period = base::TimeDelta::FromMilliseconds(10); | |
338 profile.preserve_sample_ordering = true; | |
339 | |
340 scoped_ptr<CallStackProfileMetricsProvider> provider( | |
341 new CallStackProfileMetricsProvider); | |
342 provider->SetSourceProfilesForTest(std::vector<Profile>(1, profile)); | |
343 metrics::ChromeUserMetricsExtension uma_proto; | |
344 provider->ProvideGeneralMetrics(&uma_proto); | |
345 | |
346 ASSERT_EQ(1, uma_proto.sampled_profile().size()); | |
347 const metrics::SampledProfile& sampled_profile = | |
348 uma_proto.sampled_profile().Get(0); | |
349 ASSERT_TRUE(sampled_profile.has_call_stack_profile()); | |
350 const metrics::CallStackProfile& call_stack_profile = | |
351 sampled_profile.call_stack_profile(); | |
352 | |
353 ASSERT_EQ(3, call_stack_profile.sample().size()); | |
354 for (int i = 0; i < 3; i++) { | |
355 SCOPED_TRACE("sample " + base::IntToString(i)); | |
356 const metrics::CallStackProfile::Sample& proto_sample = | |
357 call_stack_profile.sample().Get(i); | |
358 ASSERT_EQ(static_cast<int>(arraysize(sample_frames[i])), | |
359 proto_sample.entry().size()); | |
360 ASSERT_TRUE(proto_sample.has_count()); | |
361 EXPECT_EQ(i == 2 ? 2u : 1u, proto_sample.count()); | |
362 for (size_t j = 0; j < arraysize(sample_frames[i]); j++) { | |
363 SCOPED_TRACE("frame " + base::IntToString(j)); | |
364 const metrics::CallStackEntry& entry = proto_sample.entry().Get(j); | |
365 ASSERT_TRUE(entry.has_address()); | |
366 const char *instruction_pointer = reinterpret_cast<const char *>( | |
367 sample_frames[i][j].instruction_pointer); | |
368 const char *module_base_address = reinterpret_cast<const char *>( | |
369 modules[sample_frames[i][j].module_index].base_address); | |
370 EXPECT_EQ(static_cast<uint64>(instruction_pointer - module_base_address), | |
371 entry.address()); | |
372 ASSERT_TRUE(entry.has_module_id_index()); | |
373 EXPECT_EQ(sample_frames[i][j].module_index, entry.module_id_index()); | |
374 } | |
375 } | |
376 } | |
OLD | NEW |