Chromium Code Reviews| 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 |