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 Module( |
| 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 Module( |
| 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 Module( |
| 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 Module( // 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 // 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 |
| 102 // profile_modules. |
| 103 // |
| 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] = { |
| 109 { |
| 110 { |
| 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) |
| 114 }, |
| 115 { |
| 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) |
| 119 } |
| 120 }, |
| 121 { |
| 122 { |
| 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) |
| 126 }, |
| 127 { |
| 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) |
| 131 } |
| 132 } |
| 133 }; |
| 134 |
| 135 base::TimeDelta profile_durations[2] = { |
| 136 base::TimeDelta::FromMilliseconds(100), |
| 137 base::TimeDelta::FromMilliseconds(200) |
| 138 }; |
| 139 |
| 140 base::TimeDelta profile_sampling_periods[2] = { |
| 141 base::TimeDelta::FromMilliseconds(10), |
| 142 base::TimeDelta::FromMilliseconds(20) |
| 143 }; |
| 144 |
| 145 std::vector<Profile> profiles; |
| 146 for (size_t i = 0; i < arraysize(profile_sample_frames); ++i) { |
| 147 Profile profile; |
| 148 profile.modules.insert( |
| 149 profile.modules.end(), &profile_modules[i][0], |
| 150 &profile_modules[i][0] + arraysize(profile_modules[i])); |
| 151 |
| 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])); |
| 158 } |
| 159 |
| 160 profile.profile_duration = profile_durations[i]; |
| 161 profile.sampling_period = profile_sampling_periods[i]; |
| 162 profile.preserve_sample_ordering = false; |
| 163 |
| 164 profiles.push_back(profile); |
| 165 } |
| 166 |
| 167 CallStackProfileMetricsProvider provider; |
| 168 provider.SetSourceProfilesForTesting(profiles); |
| 169 ChromeUserMetricsExtension uma_proto; |
| 170 provider.ProvideGeneralMetrics(&uma_proto); |
| 171 |
| 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(); |
| 180 |
| 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] |
| 199 .base_address); |
| 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()); |
| 205 } |
| 206 } |
| 207 |
| 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()); |
| 219 } |
| 220 |
| 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()); |
| 227 } |
| 228 } |
| 229 |
| 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; |
| 234 |
| 235 const Module modules[] = { |
| 236 Module( |
| 237 reinterpret_cast<const void*>(module_base_address), |
| 238 "ABCD", |
| 239 #if defined(OS_WIN) |
| 240 base::FilePath(L"c:\\some\\path\\to\\chrome.exe") |
| 241 #else |
| 242 base::FilePath("/some/path/to/chrome") |
| 243 #endif |
| 244 ) |
| 245 }; |
| 246 |
| 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) } |
| 253 }; |
| 254 |
| 255 Profile profile; |
| 256 profile.modules.insert(profile.modules.end(), &modules[0], |
| 257 &modules[0] + arraysize(modules)); |
| 258 |
| 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])); |
| 264 } |
| 265 |
| 266 profile.profile_duration = base::TimeDelta::FromMilliseconds(100); |
| 267 profile.sampling_period = base::TimeDelta::FromMilliseconds(10); |
| 268 profile.preserve_sample_ordering = false; |
| 269 |
| 270 CallStackProfileMetricsProvider provider; |
| 271 provider.SetSourceProfilesForTesting(std::vector<Profile>(1, profile)); |
| 272 ChromeUserMetricsExtension uma_proto; |
| 273 provider.ProvideGeneralMetrics(&uma_proto); |
| 274 |
| 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(); |
| 280 |
| 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), |
| 299 entry.address()); |
| 300 ASSERT_TRUE(entry.has_module_id_index()); |
| 301 EXPECT_EQ(sample_frames[i][j].module_index, entry.module_id_index()); |
| 302 } |
| 303 } |
| 304 } |
| 305 |
| 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; |
| 310 |
| 311 const Module modules[] = { |
| 312 Module( |
| 313 reinterpret_cast<const void*>(module_base_address), |
| 314 "ABCD", |
| 315 #if defined(OS_WIN) |
| 316 base::FilePath(L"c:\\some\\path\\to\\chrome.exe") |
| 317 #else |
| 318 base::FilePath("/some/path/to/chrome") |
| 319 #endif |
| 320 ) |
| 321 }; |
| 322 |
| 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) } |
| 329 }; |
| 330 |
| 331 Profile profile; |
| 332 profile.modules.insert(profile.modules.end(), &modules[0], |
| 333 &modules[0] + arraysize(modules)); |
| 334 |
| 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])); |
| 340 } |
| 341 |
| 342 profile.profile_duration = base::TimeDelta::FromMilliseconds(100); |
| 343 profile.sampling_period = base::TimeDelta::FromMilliseconds(10); |
| 344 profile.preserve_sample_ordering = true; |
| 345 |
| 346 CallStackProfileMetricsProvider provider; |
| 347 provider.SetSourceProfilesForTesting(std::vector<Profile>(1, profile)); |
| 348 ChromeUserMetricsExtension uma_proto; |
| 349 provider.ProvideGeneralMetrics(&uma_proto); |
| 350 |
| 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(); |
| 356 |
| 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), |
| 375 entry.address()); |
| 376 ASSERT_TRUE(entry.has_module_id_index()); |
| 377 EXPECT_EQ(sample_frames[i][j].module_index, entry.module_id_index()); |
| 378 } |
| 379 } |
| 380 } |
| 381 |
| 382 |
| 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); |
| 387 |
| 388 Profile profile; |
| 389 |
| 390 profile.samples.push_back(Sample(1, frame)); |
| 391 |
| 392 profile.profile_duration = base::TimeDelta::FromMilliseconds(100); |
| 393 profile.sampling_period = base::TimeDelta::FromMilliseconds(10); |
| 394 profile.preserve_sample_ordering = false; |
| 395 |
| 396 CallStackProfileMetricsProvider provider; |
| 397 provider.SetSourceProfilesForTesting(std::vector<Profile>(1, profile)); |
| 398 ChromeUserMetricsExtension uma_proto; |
| 399 provider.ProvideGeneralMetrics(&uma_proto); |
| 400 |
| 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(); |
| 406 |
| 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()); |
| 416 } |
| 417 |
| 418 } // namespace metrics |
OLD | NEW |