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.
5 #include "components/metrics/call_stack_profile_metrics_provider.h"
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"
12 using base::StackSamplingProfiler
;
13 using Frame
= StackSamplingProfiler::Frame
;
14 using Module
= StackSamplingProfiler::Module
;
15 using Profile
= StackSamplingProfiler::Profile
;
16 using Sample
= StackSamplingProfiler::Sample
;
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;
26 const Module profile_modules
[][2] = {
29 reinterpret_cast<const void*>(module1_base_address
),
32 base::FilePath(L
"c:\\some\\path\\to\\chrome.exe")
34 base::FilePath("/some/path/to/chrome")
38 reinterpret_cast<const void*>(module2_base_address
),
41 base::FilePath(L
"c:\\some\\path\\to\\third_party.dll")
43 base::FilePath("/some/path/to/third_party.so")
49 reinterpret_cast<const void*>(module3_base_address
),
52 base::FilePath(L
"c:\\some\\path\\to\\third_party2.dll")
54 base::FilePath("/some/path/to/third_party2.so")
57 Module( // Repeated from the first profile.
58 reinterpret_cast<const void*>(module1_base_address
),
61 base::FilePath(L
"c:\\some\\path\\to\\chrome.exe")
63 base::FilePath("/some/path/to/chrome")
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
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] = {
81 0x46c3e4166659ac02ULL
,
90 0x87b66f4573a4d5caULL
,
99 // Represents two stack samples for each of two profiles, where each stack
100 // contains three frames. Each frame contains an instruction pointer and a
101 // module index corresponding to the module for the profile in
104 // So, the first stack sample below has its top frame in module 0 at an offset
105 // of 0x10 from the module's base address, the next-to-top frame in module 1
106 // at an offset of 0x20 from the module's base address, and the bottom frame
107 // in module 0 at an offset of 0x30 from the module's base address
108 const Frame profile_sample_frames
[][2][3] = {
111 Frame(reinterpret_cast<const void*>(module1_base_address
+ 0x10), 0),
112 Frame(reinterpret_cast<const void*>(module2_base_address
+ 0x20), 1),
113 Frame(reinterpret_cast<const void*>(module1_base_address
+ 0x30), 0)
116 Frame(reinterpret_cast<const void*>(module2_base_address
+ 0x10), 1),
117 Frame(reinterpret_cast<const void*>(module1_base_address
+ 0x20), 0),
118 Frame(reinterpret_cast<const void*>(module2_base_address
+ 0x30), 1)
123 Frame(reinterpret_cast<const void*>(module3_base_address
+ 0x10), 0),
124 Frame(reinterpret_cast<const void*>(module1_base_address
+ 0x20), 1),
125 Frame(reinterpret_cast<const void*>(module3_base_address
+ 0x30), 0)
128 Frame(reinterpret_cast<const void*>(module1_base_address
+ 0x10), 1),
129 Frame(reinterpret_cast<const void*>(module3_base_address
+ 0x20), 0),
130 Frame(reinterpret_cast<const void*>(module1_base_address
+ 0x30), 1)
135 base::TimeDelta profile_durations
[2] = {
136 base::TimeDelta::FromMilliseconds(100),
137 base::TimeDelta::FromMilliseconds(200)
140 base::TimeDelta profile_sampling_periods
[2] = {
141 base::TimeDelta::FromMilliseconds(10),
142 base::TimeDelta::FromMilliseconds(20)
145 std::vector
<Profile
> profiles
;
146 for (size_t i
= 0; i
< arraysize(profile_sample_frames
); ++i
) {
148 profile
.modules
.insert(
149 profile
.modules
.end(), &profile_modules
[i
][0],
150 &profile_modules
[i
][0] + arraysize(profile_modules
[i
]));
152 for (size_t j
= 0; j
< arraysize(profile_sample_frames
[i
]); ++j
) {
153 profile
.samples
.push_back(Sample());
154 Sample
& sample
= profile
.samples
.back();
155 sample
.insert(sample
.end(), &profile_sample_frames
[i
][j
][0],
156 &profile_sample_frames
[i
][j
][0] +
157 arraysize(profile_sample_frames
[i
][j
]));
160 profile
.profile_duration
= profile_durations
[i
];
161 profile
.sampling_period
= profile_sampling_periods
[i
];
162 profile
.preserve_sample_ordering
= false;
164 profiles
.push_back(profile
);
167 CallStackProfileMetricsProvider provider
;
168 provider
.SetSourceProfilesForTesting(profiles
);
169 ChromeUserMetricsExtension uma_proto
;
170 provider
.ProvideGeneralMetrics(&uma_proto
);
172 ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames
)),
173 uma_proto
.sampled_profile().size());
174 for (size_t i
= 0; i
< arraysize(profile_sample_frames
); ++i
) {
175 SCOPED_TRACE("profile " + base::IntToString(i
));
176 const SampledProfile
& sampled_profile
= uma_proto
.sampled_profile().Get(i
);
177 ASSERT_TRUE(sampled_profile
.has_call_stack_profile());
178 const CallStackProfile
& call_stack_profile
=
179 sampled_profile
.call_stack_profile();
181 ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames
[i
])),
182 call_stack_profile
.sample().size());
183 for (size_t j
= 0; j
< arraysize(profile_sample_frames
[i
]); ++j
) {
184 SCOPED_TRACE("sample " + base::IntToString(j
));
185 const CallStackProfile::Sample
& proto_sample
=
186 call_stack_profile
.sample().Get(j
);
187 ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames
[i
][j
])),
188 proto_sample
.entry().size());
189 ASSERT_TRUE(proto_sample
.has_count());
190 EXPECT_EQ(1u, proto_sample
.count());
191 for (size_t k
= 0; k
< arraysize(profile_sample_frames
[i
][j
]); ++k
) {
192 SCOPED_TRACE("frame " + base::IntToString(k
));
193 const CallStackProfile::Entry
& entry
= proto_sample
.entry().Get(k
);
194 ASSERT_TRUE(entry
.has_address());
195 const char* instruction_pointer
= reinterpret_cast<const char*>(
196 profile_sample_frames
[i
][j
][k
].instruction_pointer
);
197 const char* module_base_address
= reinterpret_cast<const char*>(
198 profile_modules
[i
][profile_sample_frames
[i
][j
][k
].module_index
]
200 EXPECT_EQ(static_cast<uint64
>(instruction_pointer
-
201 module_base_address
), entry
.address());
202 ASSERT_TRUE(entry
.has_module_id_index());
203 EXPECT_EQ(profile_sample_frames
[i
][j
][k
].module_index
,
204 entry
.module_id_index());
208 ASSERT_EQ(static_cast<int>(arraysize(profile_modules
[i
])),
209 call_stack_profile
.module_id().size());
210 for (size_t j
= 0; j
< arraysize(profile_modules
[i
]); ++j
) {
211 SCOPED_TRACE("module " + base::IntToString(j
));
212 const CallStackProfile::ModuleIdentifier
& module_identifier
=
213 call_stack_profile
.module_id().Get(j
);
214 ASSERT_TRUE(module_identifier
.has_build_id());
215 EXPECT_EQ(profile_modules
[i
][j
].id
, module_identifier
.build_id());
216 ASSERT_TRUE(module_identifier
.has_name_md5_prefix());
217 EXPECT_EQ(profile_expected_name_md5_prefixes
[i
][j
],
218 module_identifier
.name_md5_prefix());
221 ASSERT_TRUE(call_stack_profile
.has_profile_duration_ms());
222 EXPECT_EQ(profile_durations
[i
].InMilliseconds(),
223 call_stack_profile
.profile_duration_ms());
224 ASSERT_TRUE(call_stack_profile
.has_sampling_period_ms());
225 EXPECT_EQ(profile_sampling_periods
[i
].InMilliseconds(),
226 call_stack_profile
.sampling_period_ms());
230 // Checks that all duplicate samples are collapsed with
231 // preserve_sample_ordering = false.
232 TEST(CallStackProfileMetricsProviderTest
, RepeatedStacksUnordered
) {
233 const uintptr_t module_base_address
= 0x1000;
235 const Module modules
[] = {
237 reinterpret_cast<const void*>(module_base_address
),
240 base::FilePath(L
"c:\\some\\path\\to\\chrome.exe")
242 base::FilePath("/some/path/to/chrome")
247 // Duplicate samples in slots 0, 2, and 3.
248 const Frame sample_frames
[][1] = {
249 { Frame(reinterpret_cast<const void*>(module_base_address
+ 0x10), 0), },
250 { Frame(reinterpret_cast<const void*>(module_base_address
+ 0x20), 0), },
251 { Frame(reinterpret_cast<const void*>(module_base_address
+ 0x10), 0), },
252 { Frame(reinterpret_cast<const void*>(module_base_address
+ 0x10), 0) }
256 profile
.modules
.insert(profile
.modules
.end(), &modules
[0],
257 &modules
[0] + arraysize(modules
));
259 for (size_t i
= 0; i
< arraysize(sample_frames
); ++i
) {
260 profile
.samples
.push_back(Sample());
261 Sample
& sample
= profile
.samples
.back();
262 sample
.insert(sample
.end(), &sample_frames
[i
][0],
263 &sample_frames
[i
][0] + arraysize(sample_frames
[i
]));
266 profile
.profile_duration
= base::TimeDelta::FromMilliseconds(100);
267 profile
.sampling_period
= base::TimeDelta::FromMilliseconds(10);
268 profile
.preserve_sample_ordering
= false;
270 CallStackProfileMetricsProvider provider
;
271 provider
.SetSourceProfilesForTesting(std::vector
<Profile
>(1, profile
));
272 ChromeUserMetricsExtension uma_proto
;
273 provider
.ProvideGeneralMetrics(&uma_proto
);
275 ASSERT_EQ(1, uma_proto
.sampled_profile().size());
276 const SampledProfile
& sampled_profile
= uma_proto
.sampled_profile().Get(0);
277 ASSERT_TRUE(sampled_profile
.has_call_stack_profile());
278 const CallStackProfile
& call_stack_profile
=
279 sampled_profile
.call_stack_profile();
281 ASSERT_EQ(2, call_stack_profile
.sample().size());
282 for (int i
= 0; i
< 2; ++i
) {
283 SCOPED_TRACE("sample " + base::IntToString(i
));
284 const CallStackProfile::Sample
& proto_sample
=
285 call_stack_profile
.sample().Get(i
);
286 ASSERT_EQ(static_cast<int>(arraysize(sample_frames
[i
])),
287 proto_sample
.entry().size());
288 ASSERT_TRUE(proto_sample
.has_count());
289 EXPECT_EQ(i
== 0 ? 3u : 1u, proto_sample
.count());
290 for (size_t j
= 0; j
< arraysize(sample_frames
[i
]); ++j
) {
291 SCOPED_TRACE("frame " + base::IntToString(j
));
292 const CallStackProfile::Entry
& entry
= proto_sample
.entry().Get(j
);
293 ASSERT_TRUE(entry
.has_address());
294 const char* instruction_pointer
= reinterpret_cast<const char*>(
295 sample_frames
[i
][j
].instruction_pointer
);
296 const char* module_base_address
= reinterpret_cast<const char*>(
297 modules
[sample_frames
[i
][j
].module_index
].base_address
);
298 EXPECT_EQ(static_cast<uint64
>(instruction_pointer
- module_base_address
),
300 ASSERT_TRUE(entry
.has_module_id_index());
301 EXPECT_EQ(sample_frames
[i
][j
].module_index
, entry
.module_id_index());
306 // Checks that only contiguous duplicate samples are collapsed with
307 // preserve_sample_ordering = true.
308 TEST(CallStackProfileMetricsProviderTest
, RepeatedStacksOrdered
) {
309 const uintptr_t module_base_address
= 0x1000;
311 const Module modules
[] = {
313 reinterpret_cast<const void*>(module_base_address
),
316 base::FilePath(L
"c:\\some\\path\\to\\chrome.exe")
318 base::FilePath("/some/path/to/chrome")
323 // Duplicate samples in slots 0, 2, and 3.
324 const Frame sample_frames
[][1] = {
325 { Frame(reinterpret_cast<const void*>(module_base_address
+ 0x10), 0), },
326 { Frame(reinterpret_cast<const void*>(module_base_address
+ 0x20), 0), },
327 { Frame(reinterpret_cast<const void*>(module_base_address
+ 0x10), 0), },
328 { Frame(reinterpret_cast<const void*>(module_base_address
+ 0x10), 0) }
332 profile
.modules
.insert(profile
.modules
.end(), &modules
[0],
333 &modules
[0] + arraysize(modules
));
335 for (size_t i
= 0; i
< arraysize(sample_frames
); ++i
) {
336 profile
.samples
.push_back(Sample());
337 Sample
& sample
= profile
.samples
.back();
338 sample
.insert(sample
.end(), &sample_frames
[i
][0],
339 &sample_frames
[i
][0] + arraysize(sample_frames
[i
]));
342 profile
.profile_duration
= base::TimeDelta::FromMilliseconds(100);
343 profile
.sampling_period
= base::TimeDelta::FromMilliseconds(10);
344 profile
.preserve_sample_ordering
= true;
346 CallStackProfileMetricsProvider provider
;
347 provider
.SetSourceProfilesForTesting(std::vector
<Profile
>(1, profile
));
348 ChromeUserMetricsExtension uma_proto
;
349 provider
.ProvideGeneralMetrics(&uma_proto
);
351 ASSERT_EQ(1, uma_proto
.sampled_profile().size());
352 const SampledProfile
& sampled_profile
= uma_proto
.sampled_profile().Get(0);
353 ASSERT_TRUE(sampled_profile
.has_call_stack_profile());
354 const CallStackProfile
& call_stack_profile
=
355 sampled_profile
.call_stack_profile();
357 ASSERT_EQ(3, call_stack_profile
.sample().size());
358 for (int i
= 0; i
< 3; ++i
) {
359 SCOPED_TRACE("sample " + base::IntToString(i
));
360 const CallStackProfile::Sample
& proto_sample
=
361 call_stack_profile
.sample().Get(i
);
362 ASSERT_EQ(static_cast<int>(arraysize(sample_frames
[i
])),
363 proto_sample
.entry().size());
364 ASSERT_TRUE(proto_sample
.has_count());
365 EXPECT_EQ(i
== 2 ? 2u : 1u, proto_sample
.count());
366 for (size_t j
= 0; j
< arraysize(sample_frames
[i
]); ++j
) {
367 SCOPED_TRACE("frame " + base::IntToString(j
));
368 const CallStackProfile::Entry
& entry
= proto_sample
.entry().Get(j
);
369 ASSERT_TRUE(entry
.has_address());
370 const char* instruction_pointer
= reinterpret_cast<const char*>(
371 sample_frames
[i
][j
].instruction_pointer
);
372 const char* module_base_address
= reinterpret_cast<const char*>(
373 modules
[sample_frames
[i
][j
].module_index
].base_address
);
374 EXPECT_EQ(static_cast<uint64
>(instruction_pointer
- module_base_address
),
376 ASSERT_TRUE(entry
.has_module_id_index());
377 EXPECT_EQ(sample_frames
[i
][j
].module_index
, entry
.module_id_index());
383 // Checks that unknown modules produce an empty Entry.
384 TEST(CallStackProfileMetricsProviderTest
, UnknownModule
) {
385 // -1 indicates an unknown module.
386 const Frame
frame(reinterpret_cast<const void*>(0x1000), -1);
390 profile
.samples
.push_back(Sample(1, frame
));
392 profile
.profile_duration
= base::TimeDelta::FromMilliseconds(100);
393 profile
.sampling_period
= base::TimeDelta::FromMilliseconds(10);
394 profile
.preserve_sample_ordering
= false;
396 CallStackProfileMetricsProvider provider
;
397 provider
.SetSourceProfilesForTesting(std::vector
<Profile
>(1, profile
));
398 ChromeUserMetricsExtension uma_proto
;
399 provider
.ProvideGeneralMetrics(&uma_proto
);
401 ASSERT_EQ(1, uma_proto
.sampled_profile().size());
402 const SampledProfile
& sampled_profile
= uma_proto
.sampled_profile().Get(0);
403 ASSERT_TRUE(sampled_profile
.has_call_stack_profile());
404 const CallStackProfile
& call_stack_profile
=
405 sampled_profile
.call_stack_profile();
407 ASSERT_EQ(1, call_stack_profile
.sample().size());
408 const CallStackProfile::Sample
& proto_sample
=
409 call_stack_profile
.sample().Get(0);
410 ASSERT_EQ(1, proto_sample
.entry().size());
411 ASSERT_TRUE(proto_sample
.has_count());
412 EXPECT_EQ(1u, proto_sample
.count());
413 const CallStackProfile::Entry
& entry
= proto_sample
.entry().Get(0);
414 EXPECT_FALSE(entry
.has_address());
415 EXPECT_FALSE(entry
.has_module_id_index());
418 } // namespace metrics