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 "components/metrics/call_stack_profile_metrics_provider.h" | |
6 | |
7 #include "base/profiler/stack_sampling_profiler.h" | |
8 #include "base/strings/string_number_conversions.h" | |
9 #include "components/metrics/proto/chrome_user_metrics_extension.pb.h" | |
10 #include "testing/gtest/include/gtest/gtest.h" | |
11 | |
12 using base::StackSamplingProfiler; | |
13 using Frame = StackSamplingProfiler::Frame; | |
14 using Module = StackSamplingProfiler::Module; | |
15 using Profile = StackSamplingProfiler::Profile; | |
16 using Sample = StackSamplingProfiler::Sample; | |
17 | |
18 namespace metrics { | |
19 | |
20 // Checks that all properties from multiple profiles are filled as expected. | |
21 TEST(CallStackProfileMetricsProviderTest, MultipleProfiles) { | |
22 const uintptr_t module1_base_address = 0x1000; | |
23 const uintptr_t module2_base_address = 0x2000; | |
24 const uintptr_t module3_base_address = 0x3000; | |
25 | |
26 const Module profile_modules[][2] = { | |
27 { | |
28 { | |
29 reinterpret_cast<const void*>(module1_base_address), | |
30 "ABCD", | |
31 #if defined(OS_WIN) | |
32 base::FilePath(L"c:\\some\\path\\to\\chrome.exe") | |
33 #else | |
34 base::FilePath("/some/path/to/chrome") | |
35 #endif | |
36 }, | |
37 { | |
38 reinterpret_cast<const void*>(module2_base_address), | |
39 "EFGH", | |
40 #if defined(OS_WIN) | |
41 base::FilePath(L"c:\\some\\path\\to\\third_party.dll") | |
42 #else | |
43 base::FilePath("/some/path/to/third_party.so") | |
44 #endif | |
45 } | |
46 }, | |
47 { | |
48 { | |
49 reinterpret_cast<const void*>(module3_base_address), | |
50 "MNOP", | |
51 #if defined(OS_WIN) | |
52 base::FilePath(L"c:\\some\\path\\to\\third_party2.dll") | |
53 #else | |
54 base::FilePath("/some/path/to/third_party2.so") | |
55 #endif | |
56 }, | |
57 { // Repeated from the first profile. | |
58 reinterpret_cast<const void*>(module1_base_address), | |
59 "ABCD", | |
60 #if defined(OS_WIN) | |
61 base::FilePath(L"c:\\some\\path\\to\\chrome.exe") | |
62 #else | |
63 base::FilePath("/some/path/to/chrome") | |
64 #endif | |
65 }, | |
66 } | |
67 }; | |
68 | |
69 // Values for Windows generated with: | |
70 // perl -MDigest::MD5=md5 -MEncode=encode \ | |
71 // -e 'for(@ARGV){printf "%x\n", unpack "Q>", md5 encode "UTF-16LE", $_}' \ | |
72 // chrome.exe third_party.dll third_party2.dll | |
73 // | |
74 // Values for Linux generated with: | |
75 // perl -MDigest::MD5=md5 \ | |
76 // -e 'for(@ARGV){printf "%x\n", unpack "Q>", md5 $_}' \ | |
77 // chrome third_party.so third_party2.so | |
78 const uint64 profile_expected_name_md5_prefixes[][2] = { | |
79 { | |
80 #if defined(OS_WIN) | |
81 0x46c3e4166659ac02ULL, | |
82 0x7e2b8bfddeae1abaULL | |
83 #else | |
84 0x554838a8451ac36cUL, | |
85 0x843661148659c9f8UL | |
86 #endif | |
87 }, | |
88 { | |
89 #if defined(OS_WIN) | |
90 0x87b66f4573a4d5caULL, | |
91 0x46c3e4166659ac02ULL | |
92 #else | |
93 0xb4647e539fa6ec9eUL, | |
94 0x554838a8451ac36cUL | |
95 #endif | |
96 } | |
97 }; | |
98 | |
99 // Frames for all samples for all profiles in the test. | |
Ilya Sherman
2015/03/19 00:10:20
What I really meant with my previous request was,
Mike Wittman
2015/03/19 00:56:58
Done.
| |
100 const Frame profile_sample_frames[][2][3] = { | |
101 { | |
102 { | |
103 { reinterpret_cast<const void*>(module1_base_address + 0x10), 0 }, | |
104 { reinterpret_cast<const void*>(module2_base_address + 0x20), 1 }, | |
105 { reinterpret_cast<const void*>(module1_base_address + 0x30), 0 } | |
106 }, | |
107 { | |
108 { reinterpret_cast<const void*>(module2_base_address + 0x10), 1 }, | |
109 { reinterpret_cast<const void*>(module1_base_address + 0x20), 0 }, | |
110 { reinterpret_cast<const void*>(module2_base_address + 0x30), 1 } | |
111 } | |
112 }, | |
113 { | |
114 { | |
115 { reinterpret_cast<const void*>(module3_base_address + 0x10), 0 }, | |
116 { reinterpret_cast<const void*>(module1_base_address + 0x20), 1 }, | |
117 { reinterpret_cast<const void*>(module3_base_address + 0x30), 0 } | |
118 }, | |
119 { | |
120 { reinterpret_cast<const void*>(module1_base_address + 0x10), 1 }, | |
121 { reinterpret_cast<const void*>(module3_base_address + 0x20), 0 }, | |
122 { reinterpret_cast<const void*>(module1_base_address + 0x30), 1 } | |
123 } | |
124 } | |
125 }; | |
126 | |
127 base::TimeDelta profile_durations[2] = { | |
128 base::TimeDelta::FromMilliseconds(100), | |
129 base::TimeDelta::FromMilliseconds(200) | |
130 }; | |
131 | |
132 base::TimeDelta profile_sampling_periods[2] = { | |
133 base::TimeDelta::FromMilliseconds(10), | |
134 base::TimeDelta::FromMilliseconds(20) | |
135 }; | |
136 | |
137 std::vector<Profile> profiles; | |
138 for (size_t i = 0; i < arraysize(profile_sample_frames); ++i) { | |
139 Profile profile; | |
140 profile.modules.insert( | |
141 profile.modules.end(), &profile_modules[i][0], | |
142 &profile_modules[i][0] + arraysize(profile_modules[i])); | |
143 | |
144 for (size_t j = 0; j < arraysize(profile_sample_frames[i]); ++j) { | |
145 profile.samples.push_back(Sample()); | |
146 Sample& sample = profile.samples.back(); | |
147 sample.insert(sample.end(), &profile_sample_frames[i][j][0], | |
148 &profile_sample_frames[i][j][0] + | |
149 arraysize(profile_sample_frames[i][j])); | |
150 } | |
151 | |
152 profile.profile_duration = profile_durations[i]; | |
153 profile.sampling_period = profile_sampling_periods[i]; | |
154 profile.preserve_sample_ordering = false; | |
155 | |
156 profiles.push_back(profile); | |
157 } | |
158 | |
159 CallStackProfileMetricsProvider provider; | |
160 provider.SetSourceProfilesForTesting(profiles); | |
161 metrics::ChromeUserMetricsExtension uma_proto; | |
162 provider.ProvideGeneralMetrics(&uma_proto); | |
163 | |
164 ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames)), | |
165 uma_proto.sampled_profile().size()); | |
166 for (size_t i = 0; i < arraysize(profile_sample_frames); ++i) { | |
167 SCOPED_TRACE("profile " + base::IntToString(i)); | |
168 const metrics::SampledProfile& sampled_profile = | |
169 uma_proto.sampled_profile().Get(i); | |
170 ASSERT_TRUE(sampled_profile.has_call_stack_profile()); | |
171 const metrics::CallStackProfile& call_stack_profile = | |
172 sampled_profile.call_stack_profile(); | |
173 | |
174 ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames[i])), | |
175 call_stack_profile.sample().size()); | |
176 for (size_t j = 0; j < arraysize(profile_sample_frames[i]); ++j) { | |
177 SCOPED_TRACE("sample " + base::IntToString(j)); | |
178 const metrics::CallStackProfile::Sample& proto_sample = | |
179 call_stack_profile.sample().Get(j); | |
180 ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames[i][j])), | |
181 proto_sample.entry().size()); | |
182 ASSERT_TRUE(proto_sample.has_count()); | |
183 EXPECT_EQ(1u, proto_sample.count()); | |
184 for (size_t k = 0; k < arraysize(profile_sample_frames[i][j]); ++k) { | |
185 SCOPED_TRACE("frame " + base::IntToString(k)); | |
186 const metrics::CallStackEntry& entry = proto_sample.entry().Get(k); | |
187 ASSERT_TRUE(entry.has_address()); | |
188 const char* instruction_pointer = reinterpret_cast<const char*>( | |
189 profile_sample_frames[i][j][k].instruction_pointer); | |
190 const char* module_base_address = reinterpret_cast<const char*>( | |
191 profile_modules[i][profile_sample_frames[i][j][k].module_index] | |
192 .base_address); | |
193 EXPECT_EQ(static_cast<uint64>(instruction_pointer - | |
194 module_base_address), entry.address()); | |
195 ASSERT_TRUE(entry.has_module_id_index()); | |
196 EXPECT_EQ(profile_sample_frames[i][j][k].module_index, | |
197 entry.module_id_index()); | |
198 } | |
199 } | |
200 | |
201 ASSERT_EQ(static_cast<int>(arraysize(profile_modules[i])), | |
202 call_stack_profile.module_id().size()); | |
203 for (size_t j = 0; j < arraysize(profile_modules[i]); ++j) { | |
204 SCOPED_TRACE("module " + base::IntToString(j)); | |
205 const metrics::ModuleIdentifier& module_identifier = | |
206 call_stack_profile.module_id().Get(j); | |
207 ASSERT_TRUE(module_identifier.has_build_id()); | |
208 EXPECT_EQ(profile_modules[i][j].id, module_identifier.build_id()); | |
209 ASSERT_TRUE(module_identifier.has_name_md5_prefix()); | |
210 EXPECT_EQ(profile_expected_name_md5_prefixes[i][j], | |
211 module_identifier.name_md5_prefix()); | |
212 } | |
213 | |
214 ASSERT_TRUE(call_stack_profile.has_profile_duration_ms()); | |
215 EXPECT_EQ(profile_durations[i].InMilliseconds(), | |
216 call_stack_profile.profile_duration_ms()); | |
217 ASSERT_TRUE(call_stack_profile.has_sampling_period_ms()); | |
218 EXPECT_EQ(profile_sampling_periods[i].InMilliseconds(), | |
219 call_stack_profile.sampling_period_ms()); | |
220 } | |
221 } | |
222 | |
223 // Checks that all duplicate samples are collapsed with | |
224 // preserve_sample_ordering = false. | |
225 TEST(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) { | |
226 const uintptr_t module_base_address = 0x1000; | |
227 | |
228 const Module modules[] = { | |
229 { | |
230 reinterpret_cast<const void*>(module_base_address), | |
231 "ABCD", | |
232 #if defined(OS_WIN) | |
233 base::FilePath(L"c:\\some\\path\\to\\chrome.exe") | |
234 #else | |
235 base::FilePath("/some/path/to/chrome") | |
236 #endif | |
237 } | |
238 }; | |
239 | |
240 // Duplicate samples in slots 0, 2, and 3. | |
241 const Frame sample_frames[][1] = { | |
242 { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, }, | |
243 { { reinterpret_cast<const void*>(module_base_address + 0x20), 0 }, }, | |
244 { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, }, | |
245 { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, }, | |
246 }; | |
247 | |
248 Profile profile; | |
249 profile.modules.insert(profile.modules.end(), &modules[0], | |
250 &modules[0] + arraysize(modules)); | |
251 | |
252 for (size_t i = 0; i < arraysize(sample_frames); ++i) { | |
253 profile.samples.push_back(Sample()); | |
254 Sample& sample = profile.samples.back(); | |
255 sample.insert(sample.end(), &sample_frames[i][0], | |
256 &sample_frames[i][0] + arraysize(sample_frames[i])); | |
257 } | |
258 | |
259 profile.profile_duration = base::TimeDelta::FromMilliseconds(100); | |
260 profile.sampling_period = base::TimeDelta::FromMilliseconds(10); | |
261 profile.preserve_sample_ordering = false; | |
262 | |
263 CallStackProfileMetricsProvider provider; | |
264 provider.SetSourceProfilesForTesting(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 CallStackProfileMetricsProvider provider; | |
341 provider.SetSourceProfilesForTesting(std::vector<Profile>(1, profile)); | |
342 metrics::ChromeUserMetricsExtension uma_proto; | |
343 provider.ProvideGeneralMetrics(&uma_proto); | |
344 | |
345 ASSERT_EQ(1, uma_proto.sampled_profile().size()); | |
346 const metrics::SampledProfile& sampled_profile = | |
347 uma_proto.sampled_profile().Get(0); | |
348 ASSERT_TRUE(sampled_profile.has_call_stack_profile()); | |
349 const metrics::CallStackProfile& call_stack_profile = | |
350 sampled_profile.call_stack_profile(); | |
351 | |
352 ASSERT_EQ(3, call_stack_profile.sample().size()); | |
353 for (int i = 0; i < 3; ++i) { | |
354 SCOPED_TRACE("sample " + base::IntToString(i)); | |
355 const metrics::CallStackProfile::Sample& proto_sample = | |
356 call_stack_profile.sample().Get(i); | |
357 ASSERT_EQ(static_cast<int>(arraysize(sample_frames[i])), | |
358 proto_sample.entry().size()); | |
359 ASSERT_TRUE(proto_sample.has_count()); | |
360 EXPECT_EQ(i == 2 ? 2u : 1u, proto_sample.count()); | |
361 for (size_t j = 0; j < arraysize(sample_frames[i]); ++j) { | |
362 SCOPED_TRACE("frame " + base::IntToString(j)); | |
363 const metrics::CallStackEntry& entry = proto_sample.entry().Get(j); | |
364 ASSERT_TRUE(entry.has_address()); | |
365 const char* instruction_pointer = reinterpret_cast<const char*>( | |
366 sample_frames[i][j].instruction_pointer); | |
367 const char* module_base_address = reinterpret_cast<const char*>( | |
368 modules[sample_frames[i][j].module_index].base_address); | |
369 EXPECT_EQ(static_cast<uint64>(instruction_pointer - module_base_address), | |
370 entry.address()); | |
371 ASSERT_TRUE(entry.has_module_id_index()); | |
372 EXPECT_EQ(sample_frames[i][j].module_index, entry.module_id_index()); | |
373 } | |
374 } | |
375 } | |
376 | |
377 | |
378 // Checks that unknown modules produce an empty CallStackEntry. | |
379 TEST(CallStackProfileMetricsProviderTest, UnknownModule) { | |
380 // -1 indicates an unknown module. | |
381 const Frame frame = { reinterpret_cast<const void*>(0x1000), -1 }; | |
382 | |
383 Profile profile; | |
384 | |
385 profile.samples.push_back(Sample(1, frame)); | |
386 | |
387 profile.profile_duration = base::TimeDelta::FromMilliseconds(100); | |
388 profile.sampling_period = base::TimeDelta::FromMilliseconds(10); | |
389 profile.preserve_sample_ordering = false; | |
390 | |
391 CallStackProfileMetricsProvider provider; | |
392 provider.SetSourceProfilesForTesting(std::vector<Profile>(1, profile)); | |
393 metrics::ChromeUserMetricsExtension uma_proto; | |
394 provider.ProvideGeneralMetrics(&uma_proto); | |
395 | |
396 ASSERT_EQ(1, uma_proto.sampled_profile().size()); | |
397 const metrics::SampledProfile& sampled_profile = | |
398 uma_proto.sampled_profile().Get(0); | |
399 ASSERT_TRUE(sampled_profile.has_call_stack_profile()); | |
400 const metrics::CallStackProfile& call_stack_profile = | |
401 sampled_profile.call_stack_profile(); | |
402 | |
403 ASSERT_EQ(1u, call_stack_profile.sample().size()); | |
404 const metrics::CallStackProfile::Sample& proto_sample = | |
405 call_stack_profile.sample().Get(0); | |
406 ASSERT_EQ(1u, proto_sample.entry().size()); | |
407 ASSERT_TRUE(proto_sample.has_count()); | |
408 EXPECT_EQ(1u, proto_sample.count()); | |
409 const metrics::CallStackEntry& entry = proto_sample.entry().Get(0); | |
410 EXPECT_FALSE(entry.has_address()); | |
411 EXPECT_FALSE(entry.has_module_id_index()); | |
412 } | |
413 | |
414 } // namespace metrics | |
OLD | NEW |