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 "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 |