Chromium Code Reviews| Index: components/metrics/call_stack_profile_metrics_provider_unittest.cc |
| diff --git a/components/metrics/call_stack_profile_metrics_provider_unittest.cc b/components/metrics/call_stack_profile_metrics_provider_unittest.cc |
| index 3e515b5c2db336cc09e2b1cbf66d5e4437cfa6af..eadfd377e26602185c1a07258f19e422ad67af6c 100644 |
| --- a/components/metrics/call_stack_profile_metrics_provider_unittest.cc |
| +++ b/components/metrics/call_stack_profile_metrics_provider_unittest.cc |
| @@ -27,6 +27,64 @@ using Profile = StackSamplingProfiler::CallStackProfile; |
| using Profiles = StackSamplingProfiler::CallStackProfiles; |
| using Sample = StackSamplingProfiler::Sample; |
| +namespace { |
| + |
| +using ProfilesGenerator = uint64_t; |
| + |
| +enum : ProfilesGenerator { |
| + kEndOfGenerator, |
| + kAddPhase, |
| + kBeginActivity, |
| + kEndActivity, |
| + kNewProfile, |
| + kNewSample, |
| + kNextFrame, |
| + kDefineModule, |
| +}; |
| + |
| +struct ExpectedProtoModule { |
| + const char* build_id; |
| + uint64_t name_md5; |
| + uint64_t base_address; |
| +}; |
| + |
| +struct ExpectedProtoEntry { |
| + int32_t module_index; |
| + uint64_t address; |
| +}; |
| + |
| +struct ExpectedProtoSample { |
| + uint32_t process_phases; // Bit-field of expected phases. |
| + uint32_t activities_begun; // Bit-field of expected activities begun. |
| + uint32_t activities_ended; // Bit-field of expected activities ended. |
| + const ExpectedProtoEntry* entries; |
| + int entry_count; |
| + int64_t entry_repeats; |
| +}; |
| + |
| +struct ExpectedProtoProfile { |
| + int32_t duration_ms; |
| + int32_t period_ms; |
| + const ExpectedProtoModule* modules; |
| + int module_count; |
| + const ExpectedProtoSample* samples; |
| + int sample_count; |
| +}; |
| + |
| +} // namespace |
| + |
| +#define GEN_ADD_PHASE(phase) kAddPhase, phase |
|
Alexei Svitkine (slow)
2016/11/07 23:22:07
What is this for? I don't see it being used.
bcwhite
2016/11/08 01:02:36
It's not... yet. It'll be needed when the remaini
|
| +#define GEN_BEGIN_ACTIVITY(activity) kBeginActivity, activity |
| +#define GEN_END_ACTIVITY(activity) kEndActivity activity |
| +#define GEN_NEW_PROFILE(duration_ms, interval_ms) \ |
| + kNewProfile, duration_ms, interval_ms |
| +#define GEN_NEW_SAMPLE() kNewSample |
| +#define GEN_FRAME(module, offset) kNextFrame, module, offset |
| +#define GEN_DEFINE_MODULE(name, path, base) \ |
| + kDefineModule, reinterpret_cast<uintptr_t>(name), \ |
| + reinterpret_cast<uintptr_t>(&path), base |
| +#define GEN_END() kEndOfGenerator |
| + |
| namespace metrics { |
| // This test fixture enables the field trial that |
| @@ -49,6 +107,13 @@ class CallStackProfileMetricsProviderTest : public testing::Test { |
| std::move(profiles)); |
| } |
| + // Utility to create a Profiles list from a generator constant. |
| + void GenerateProfiles(std::vector<Profile>* profiles, |
| + const ProfilesGenerator* generator); |
| + |
| + void VerifyProfileProto(const SampledProfile& proto, |
| + const ExpectedProtoProfile& expected); |
| + |
| private: |
| // Exposes field trial/group names from the CallStackProfileMetricsProvider. |
| class TestState : public CallStackProfileMetricsProvider { |
| @@ -63,54 +128,120 @@ class CallStackProfileMetricsProviderTest : public testing::Test { |
| DISALLOW_COPY_AND_ASSIGN(CallStackProfileMetricsProviderTest); |
| }; |
| +void CallStackProfileMetricsProviderTest::GenerateProfiles( |
| + std::vector<Profile>* profiles, |
| + const ProfilesGenerator* generator) { |
| + uint32_t process_phases = 0; |
| + uint32_t current_activities = 0; |
| + Profile* profile = nullptr; |
| + |
| + while (true) { |
| + switch (*generator++) { |
| + case kEndOfGenerator: |
| + return; |
| + case kAddPhase: |
| + process_phases |= 1 << *generator++; |
| + break; |
| + case kBeginActivity: |
| + current_activities |= 1 << *generator++; |
| + break; |
| + case kEndActivity: |
| + current_activities &= ~(1 << *generator++); |
| + break; |
| + case kNewProfile: |
| + profiles->push_back(Profile()); |
| + profile = &profiles->back(); |
| + profile->profile_duration = |
| + base::TimeDelta::FromMilliseconds(*generator++); |
| + profile->sampling_period = |
| + base::TimeDelta::FromMilliseconds(*generator++); |
| + break; |
| + case kNewSample: |
| + profile->samples.push_back(Sample()); |
| + break; |
| + case kNextFrame: { |
| + size_t index = static_cast<size_t>(*generator++); |
| + uintptr_t address = static_cast<uintptr_t>(*generator++); |
| + profile->samples.back().frames.push_back(Frame(address, index)); |
| + } break; |
| + case kDefineModule: { |
| + const char* name = |
| + reinterpret_cast<const char*>(static_cast<uintptr_t>(*generator++)); |
| + const base::FilePath* path = reinterpret_cast<const base::FilePath*>( |
| + static_cast<uintptr_t>(*generator++)); |
| + uintptr_t base = static_cast<uintptr_t>(*generator++); |
| + profile->modules.push_back(Module(base, name, *path)); |
| + } break; |
| + default: |
| + NOTREACHED(); |
| + }; |
| + } |
| +} |
| + |
| +void CallStackProfileMetricsProviderTest::VerifyProfileProto( |
| + const SampledProfile& proto, |
| + const ExpectedProtoProfile& expected) { |
| + ASSERT_TRUE(proto.has_call_stack_profile()); |
| + const CallStackProfile& stack = proto.call_stack_profile(); |
| + |
| + ASSERT_TRUE(stack.has_profile_duration_ms()); |
| + EXPECT_EQ(expected.duration_ms, stack.profile_duration_ms()); |
| + ASSERT_TRUE(stack.has_sampling_period_ms()); |
| + EXPECT_EQ(expected.period_ms, stack.sampling_period_ms()); |
| + |
| + ASSERT_EQ(expected.module_count, stack.module_id().size()); |
| + for (int m = 0; m < expected.module_count; ++m) { |
| + SCOPED_TRACE("module " + base::IntToString(m)); |
| + const CallStackProfile::ModuleIdentifier& module_id = |
| + stack.module_id().Get(m); |
| + ASSERT_TRUE(module_id.has_build_id()); |
| + EXPECT_EQ(expected.modules[m].build_id, module_id.build_id()); |
| + ASSERT_TRUE(module_id.has_name_md5_prefix()); |
| + EXPECT_EQ(expected.modules[m].name_md5, module_id.name_md5_prefix()); |
| + } |
| + |
| + ASSERT_EQ(expected.sample_count, stack.sample().size()); |
| + for (int s = 0; s < expected.sample_count; ++s) { |
| + SCOPED_TRACE("sample " + base::IntToString(s)); |
| + const CallStackProfile::Sample& proto_sample = stack.sample().Get(s); |
| + |
| + uint32_t process_phases = 0; |
| + uint32_t activities_begun = 0; |
| + uint32_t activities_ended = 0; |
| + for (int i = 0; i < proto_sample.process_phase().size(); ++i) |
| + process_phases |= 1U << proto_sample.process_phase().Get(i); |
| + for (int i = 0; i < proto_sample.activity_begun().size(); ++i) |
| + activities_begun |= 1U << proto_sample.activity_begun().Get(i); |
| + for (int i = 0; i < proto_sample.activity_ended().size(); ++i) |
| + activities_ended |= 1U << proto_sample.activity_ended().Get(i); |
| + EXPECT_EQ(expected.samples[s].process_phases, process_phases); |
| + EXPECT_EQ(expected.samples[s].activities_begun, activities_begun); |
| + EXPECT_EQ(expected.samples[s].activities_ended, activities_ended); |
| + |
| + ASSERT_EQ(expected.samples[s].entry_count, proto_sample.entry().size()); |
| + ASSERT_TRUE(proto_sample.has_count()); |
| + EXPECT_EQ(expected.samples[s].entry_repeats, proto_sample.count()); |
| + for (int e = 0; e < expected.samples[s].entry_count; ++e) { |
| + SCOPED_TRACE("entry " + base::SizeTToString(e)); |
| + const CallStackProfile::Entry& entry = proto_sample.entry().Get(e); |
| + ASSERT_TRUE(entry.has_module_id_index()); |
| + EXPECT_EQ(expected.samples[s].entries[e].module_index, |
| + entry.module_id_index()); |
| + ASSERT_TRUE(entry.has_address()); |
| + EXPECT_EQ(expected.samples[s].entries[e].address, entry.address()); |
| + } |
| + } |
| +} |
| + |
| // Checks that all properties from multiple profiles are filled as expected. |
| TEST_F(CallStackProfileMetricsProviderTest, MultipleProfiles) { |
| - const uintptr_t module1_base_address = 0x1000; |
| - const uintptr_t module2_base_address = 0x2000; |
| - const uintptr_t module3_base_address = 0x3000; |
| - |
| - const Module profile_modules[][2] = { |
| - { |
| - Module( |
| - module1_base_address, |
| - "ABCD", |
| -#if defined(OS_WIN) |
| - base::FilePath(L"c:\\some\\path\\to\\chrome.exe") |
| -#else |
| - base::FilePath("/some/path/to/chrome") |
| -#endif |
| - ), |
| - Module( |
| - module2_base_address, |
| - "EFGH", |
| -#if defined(OS_WIN) |
| - base::FilePath(L"c:\\some\\path\\to\\third_party.dll") |
| -#else |
| - base::FilePath("/some/path/to/third_party.so") |
| -#endif |
| - ), |
| - }, |
| - { |
| - Module( |
| - module3_base_address, |
| - "MNOP", |
| -#if defined(OS_WIN) |
| - base::FilePath(L"c:\\some\\path\\to\\third_party2.dll") |
| -#else |
| - base::FilePath("/some/path/to/third_party2.so") |
| -#endif |
| - ), |
| - Module( // Repeated from the first profile. |
| - module1_base_address, |
| - "ABCD", |
| -#if defined(OS_WIN) |
| - base::FilePath(L"c:\\some\\path\\to\\chrome.exe") |
| -#else |
| - base::FilePath("/some/path/to/chrome") |
| -#endif |
| - ) |
| - } |
| - }; |
| + const size_t profiles_count = 2; |
| + const uintptr_t moduleA_base_address = 0x1000; |
| + const uintptr_t moduleB_base_address = 0x2000; |
| + const uintptr_t moduleC_base_address = 0x3000; |
| + const char* moduleA_name = "ABCD"; |
| + const char* moduleB_name = "EFGH"; |
| + const char* moduleC_name = "MNOP"; |
| // Values for Windows generated with: |
| // perl -MDigest::MD5=md5 -MEncode=encode |
| @@ -121,21 +252,21 @@ TEST_F(CallStackProfileMetricsProviderTest, MultipleProfiles) { |
| // perl -MDigest::MD5=md5 |
| // -e 'for(@ARGV){printf "%x\n", unpack "Q>", md5 $_}' |
| // chrome third_party.so third_party2.so |
| - const uint64_t profile_expected_name_md5_prefixes[][2] = { |
| - { |
| -#if defined(OS_WIN) |
| - 0x46c3e4166659ac02ULL, 0x7e2b8bfddeae1abaULL |
| -#else |
| - 0x554838a8451ac36cUL, 0x843661148659c9f8UL |
| -#endif |
| - }, |
| - { |
| #if defined(OS_WIN) |
| - 0x87b66f4573a4d5caULL, 0x46c3e4166659ac02ULL |
| + uint64_t moduleA_md5 = 0x46C3E4166659AC02ULL; |
| + uint64_t moduleB_md5 = 0x7E2B8BFDDEAE1ABAULL; |
| + uint64_t moduleC_md5 = 0x87B66F4573A4D5CAULL; |
| + base::FilePath moduleA_path(L"c:\\some\\path\\to\\chrome.exe"); |
| + base::FilePath moduleB_path(L"c:\\some\\path\\to\\third_party.dll"); |
| + base::FilePath moduleC_path(L"c:\\some\\path\\to\\third_party2.dll"); |
| #else |
| - 0xb4647e539fa6ec9eUL, 0x554838a8451ac36cUL |
| + uint64_t moduleA_md5 = 0x554838A8451AC36CULL; |
| + uint64_t moduleB_md5 = 0x843661148659C9F8ULL; |
| + uint64_t moduleC_md5 = 0xB4647E539FA6EC9EULL; |
| + base::FilePath moduleA_path("/some/path/to/chrome"); |
| + base::FilePath moduleB_path("/some/path/to/third_party.so"); |
| + base::FilePath moduleC_path("/some/path/to/third_party2.so"); |
| #endif |
| - }}; |
| // Represents two stack samples for each of two profiles, where each stack |
| // contains three frames. Each frame contains an instruction pointer and a |
| @@ -146,63 +277,104 @@ TEST_F(CallStackProfileMetricsProviderTest, MultipleProfiles) { |
| // of 0x10 from the module's base address, the next-to-top frame in module 1 |
| // at an offset of 0x20 from the module's base address, and the bottom frame |
| // in module 0 at an offset of 0x30 from the module's base address |
| - const Frame profile_sample_frames[][2][3] = { |
| - { |
| + const ProfilesGenerator test_profiles_generator[] = { |
| + GEN_NEW_PROFILE(100, 10), |
| + GEN_DEFINE_MODULE(moduleA_name, moduleA_path, moduleA_base_address), |
| + GEN_DEFINE_MODULE(moduleB_name, moduleB_path, moduleB_base_address), |
| + |
| + GEN_NEW_SAMPLE(), |
| + GEN_FRAME(0, moduleA_base_address + 0x10), |
| + GEN_FRAME(1, moduleB_base_address + 0x20), |
| + GEN_FRAME(0, moduleA_base_address + 0x30), |
| + GEN_NEW_SAMPLE(), |
| + GEN_FRAME(1, moduleB_base_address + 0x10), |
| + GEN_FRAME(0, moduleA_base_address + 0x20), |
| + GEN_FRAME(1, moduleB_base_address + 0x30), |
| + |
| + GEN_NEW_PROFILE(200, 20), |
| + GEN_DEFINE_MODULE(moduleC_name, moduleC_path, moduleC_base_address), |
| + GEN_DEFINE_MODULE(moduleA_name, moduleA_path, moduleA_base_address), |
| + |
| + GEN_NEW_SAMPLE(), |
| + GEN_FRAME(0, moduleC_base_address + 0x10), |
| + GEN_FRAME(1, moduleA_base_address + 0x20), |
| + GEN_FRAME(0, moduleC_base_address + 0x30), |
| + GEN_NEW_SAMPLE(), |
| + GEN_FRAME(1, moduleA_base_address + 0x10), |
| + GEN_FRAME(0, moduleC_base_address + 0x20), |
| + GEN_FRAME(1, moduleA_base_address + 0x30), |
| + |
| + GEN_END(), |
| + }; |
| + |
| + const ExpectedProtoModule expected_proto_modules1[] = { |
| + { moduleA_name, moduleA_md5, moduleA_base_address }, |
| + { moduleB_name, moduleB_md5, moduleB_base_address } |
| + }; |
| + |
| + const ExpectedProtoEntry expected_proto_entries11[] = { |
| + {0, 0x10}, |
| + {1, 0x20}, |
| + {0, 0x30}, |
| + }; |
| + const ExpectedProtoEntry expected_proto_entries12[] = { |
| + {1, 0x10}, |
| + {0, 0x20}, |
| + {1, 0x30}, |
| + }; |
| + const ExpectedProtoSample expected_proto_samples1[] = { |
| { |
| - Frame(module1_base_address + 0x10, 0), |
| - Frame(module2_base_address + 0x20, 1), |
| - Frame(module1_base_address + 0x30, 0) |
| + 0, 0, 0, |
| + expected_proto_entries11, arraysize(expected_proto_entries11), 1, |
| }, |
| { |
| - Frame(module2_base_address + 0x10, 1), |
| - Frame(module1_base_address + 0x20, 0), |
| - Frame(module2_base_address + 0x30, 1) |
| - } |
| - }, |
| - { |
| - { |
| - Frame(module3_base_address + 0x10, 0), |
| - Frame(module1_base_address + 0x20, 1), |
| - Frame(module3_base_address + 0x30, 0) |
| + 0, 0, 0, |
| + expected_proto_entries12, arraysize(expected_proto_entries12), 1, |
| }, |
| - { |
| - Frame(module1_base_address + 0x10, 1), |
| - Frame(module3_base_address + 0x20, 0), |
| - Frame(module1_base_address + 0x30, 1) |
| - } |
| - } |
| }; |
| - base::TimeDelta profile_durations[2] = { |
| - base::TimeDelta::FromMilliseconds(100), |
| - base::TimeDelta::FromMilliseconds(200) |
| + const ExpectedProtoModule expected_proto_modules2[] = { |
| + { moduleC_name, moduleC_md5, moduleC_base_address }, |
| + { moduleA_name, moduleA_md5, moduleA_base_address } |
| }; |
| - base::TimeDelta profile_sampling_periods[2] = { |
| - base::TimeDelta::FromMilliseconds(10), |
| - base::TimeDelta::FromMilliseconds(20) |
| + const ExpectedProtoEntry expected_proto_entries21[] = { |
| + {0, 0x10}, |
| + {1, 0x20}, |
| + {0, 0x30}, |
| + }; |
| + const ExpectedProtoEntry expected_proto_entries22[] = { |
| + {1, 0x10}, |
| + {0, 0x20}, |
| + {1, 0x30}, |
| + }; |
| + const ExpectedProtoSample expected_proto_samples2[] = { |
| + { |
| + 0, 0, 0, |
| + expected_proto_entries11, arraysize(expected_proto_entries21), 1, |
| + }, |
| + { |
| + 0, 0, 0, |
| + expected_proto_entries12, arraysize(expected_proto_entries22), 1, |
| + }, |
| }; |
| - std::vector<Profile> profiles; |
| - for (size_t i = 0; i < arraysize(profile_sample_frames); ++i) { |
| - Profile profile; |
| - profile.modules.insert( |
| - profile.modules.end(), &profile_modules[i][0], |
| - &profile_modules[i][0] + arraysize(profile_modules[i])); |
| - |
| - for (size_t j = 0; j < arraysize(profile_sample_frames[i]); ++j) { |
| - profile.samples.push_back(Sample()); |
| - Sample& sample = profile.samples.back(); |
| - sample.insert(sample.end(), &profile_sample_frames[i][j][0], |
| - &profile_sample_frames[i][j][0] + |
| - arraysize(profile_sample_frames[i][j])); |
| - } |
| - |
| - profile.profile_duration = profile_durations[i]; |
| - profile.sampling_period = profile_sampling_periods[i]; |
| + const ExpectedProtoProfile expected_proto_profiles[] = { |
| + { |
| + 100, 10, |
| + expected_proto_modules1, arraysize(expected_proto_modules1), |
| + expected_proto_samples1, arraysize(expected_proto_samples1), |
| + }, |
| + { |
| + 200, 20, |
| + expected_proto_modules2, arraysize(expected_proto_modules2), |
| + expected_proto_samples2, arraysize(expected_proto_samples2), |
| + }, |
| + }; |
| - profiles.push_back(std::move(profile)); |
| - } |
| + std::vector<Profile> profiles; |
| + GenerateProfiles(&profiles, test_profiles_generator); |
| + ASSERT_EQ(profiles_count, profiles.size()); |
| CallStackProfileMetricsProvider provider; |
| provider.OnRecordingEnabled(); |
| @@ -215,68 +387,12 @@ TEST_F(CallStackProfileMetricsProviderTest, MultipleProfiles) { |
| ChromeUserMetricsExtension uma_proto; |
| provider.ProvideGeneralMetrics(&uma_proto); |
| - ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames)), |
| + ASSERT_EQ(static_cast<int>(profiles_count), |
| uma_proto.sampled_profile().size()); |
| - for (size_t i = 0; i < arraysize(profile_sample_frames); ++i) { |
| - SCOPED_TRACE("profile " + base::SizeTToString(i)); |
| - const SampledProfile& sampled_profile = uma_proto.sampled_profile().Get(i); |
| - ASSERT_TRUE(sampled_profile.has_call_stack_profile()); |
| - const CallStackProfile& call_stack_profile = |
| - sampled_profile.call_stack_profile(); |
| - |
| - ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames[i])), |
| - call_stack_profile.sample().size()); |
| - for (size_t j = 0; j < arraysize(profile_sample_frames[i]); ++j) { |
| - SCOPED_TRACE("sample " + base::SizeTToString(j)); |
| - const CallStackProfile::Sample& proto_sample = |
| - call_stack_profile.sample().Get(j); |
| - ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames[i][j])), |
| - proto_sample.entry().size()); |
| - ASSERT_TRUE(proto_sample.has_count()); |
| - EXPECT_EQ(1u, proto_sample.count()); |
| - for (size_t k = 0; k < arraysize(profile_sample_frames[i][j]); ++k) { |
| - SCOPED_TRACE("frame " + base::SizeTToString(k)); |
| - const CallStackProfile::Entry& entry = proto_sample.entry().Get(k); |
| - ASSERT_TRUE(entry.has_address()); |
| - const char* instruction_pointer = reinterpret_cast<const char*>( |
| - profile_sample_frames[i][j][k].instruction_pointer); |
| - const char* module_base_address = reinterpret_cast<const char*>( |
| - profile_modules[i][profile_sample_frames[i][j][k].module_index] |
| - .base_address); |
| - EXPECT_EQ( |
| - static_cast<uint64_t>(instruction_pointer - module_base_address), |
| - entry.address()); |
| - ASSERT_TRUE(entry.has_module_id_index()); |
| - EXPECT_EQ(profile_sample_frames[i][j][k].module_index, |
| - static_cast<size_t>(entry.module_id_index())); |
| - } |
| - } |
| - |
| - ASSERT_EQ(static_cast<int>(arraysize(profile_modules[i])), |
| - call_stack_profile.module_id().size()); |
| - for (size_t j = 0; j < arraysize(profile_modules[i]); ++j) { |
| - SCOPED_TRACE("module " + base::SizeTToString(j)); |
| - const CallStackProfile::ModuleIdentifier& module_identifier = |
| - call_stack_profile.module_id().Get(j); |
| - ASSERT_TRUE(module_identifier.has_build_id()); |
| - EXPECT_EQ(profile_modules[i][j].id, module_identifier.build_id()); |
| - ASSERT_TRUE(module_identifier.has_name_md5_prefix()); |
| - EXPECT_EQ(profile_expected_name_md5_prefixes[i][j], |
| - module_identifier.name_md5_prefix()); |
| - } |
| - |
| - ASSERT_TRUE(call_stack_profile.has_profile_duration_ms()); |
| - EXPECT_EQ(profile_durations[i].InMilliseconds(), |
| - call_stack_profile.profile_duration_ms()); |
| - ASSERT_TRUE(call_stack_profile.has_sampling_period_ms()); |
| - EXPECT_EQ(profile_sampling_periods[i].InMilliseconds(), |
| - call_stack_profile.sampling_period_ms()); |
| - ASSERT_TRUE(sampled_profile.has_process()); |
| - EXPECT_EQ(BROWSER_PROCESS, sampled_profile.process()); |
| - ASSERT_TRUE(sampled_profile.has_thread()); |
| - EXPECT_EQ(UI_THREAD, sampled_profile.thread()); |
| - ASSERT_TRUE(sampled_profile.has_trigger_event()); |
| - EXPECT_EQ(SampledProfile::PROCESS_STARTUP, sampled_profile.trigger_event()); |
| + for (size_t p = 0; p < profiles_count; ++p) { |
| + SCOPED_TRACE("profile " + base::SizeTToString(p)); |
| + VerifyProfileProto(uma_proto.sampled_profile().Get(p), |
| + expected_proto_profiles[p]); |
| } |
| } |
| @@ -297,12 +413,32 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) { |
| ) |
| }; |
| - // Duplicate samples in slots 0, 2, and 3. |
| + // Samples are unique only if the phase, activity, and frame all match. |
| + const uint32_t profile_phases_and_activities[][2] = { |
| + { 1, 0 }, |
| + { 1, 0 }, |
| + { 3, 0 }, |
| + { 3, 0 }, |
| + { 3, 1 }, |
| + // Unique above, duplicates below. |
| + { 1, 0 }, |
| + { 1, 0 }, |
| + { 3, 0 }, |
| + }; |
| const Frame sample_frames[][1] = { |
| { Frame(module_base_address + 0x10, 0), }, |
| { Frame(module_base_address + 0x20, 0), }, |
| { Frame(module_base_address + 0x10, 0), }, |
| - { Frame(module_base_address + 0x10, 0) } |
| + { Frame(module_base_address + 0x20, 0), }, |
| + { Frame(module_base_address + 0x10, 0), }, |
| + // Unique above, duplicates below. |
| + { Frame(module_base_address + 0x10, 0), }, |
| + { Frame(module_base_address + 0x10, 0), }, |
| + { Frame(module_base_address + 0x10, 0), }, |
| + }; |
| + |
| + const int duplicate_counts[] = { |
| + 3, 1, 2, 1, 1, |
| }; |
| Profile profile; |
| @@ -312,8 +448,10 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) { |
| for (size_t i = 0; i < arraysize(sample_frames); ++i) { |
| profile.samples.push_back(Sample()); |
| Sample& sample = profile.samples.back(); |
| - sample.insert(sample.end(), &sample_frames[i][0], |
| - &sample_frames[i][0] + arraysize(sample_frames[i])); |
| + sample.process_phases = profile_phases_and_activities[i][0]; |
| + sample.current_activities = profile_phases_and_activities[i][1]; |
| + sample.frames.insert(sample.frames.end(), &sample_frames[i][0], |
| + &sample_frames[i][0] + arraysize(sample_frames[i])); |
| } |
| profile.profile_duration = base::TimeDelta::FromMilliseconds(100); |
| @@ -338,15 +476,21 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) { |
| const CallStackProfile& call_stack_profile = |
| sampled_profile.call_stack_profile(); |
| - ASSERT_EQ(2, call_stack_profile.sample().size()); |
| - for (int i = 0; i < 2; ++i) { |
| - SCOPED_TRACE("sample " + base::IntToString(i)); |
| + ASSERT_EQ(static_cast<int>(arraysize(duplicate_counts)), |
| + call_stack_profile.sample().size()); |
| + for (size_t i = 0; i < arraysize(duplicate_counts); ++i) { |
| + SCOPED_TRACE("sample " + base::SizeTToString(i)); |
| const CallStackProfile::Sample& proto_sample = |
| call_stack_profile.sample().Get(i); |
| + |
| + // For MAY_SHUFFLE, these are reset with every sample. |
| + uint32_t process_phase = 0; |
| + uint32_t current_activities = 0; |
| + |
| ASSERT_EQ(static_cast<int>(arraysize(sample_frames[i])), |
| proto_sample.entry().size()); |
| ASSERT_TRUE(proto_sample.has_count()); |
| - EXPECT_EQ(i == 0 ? 3u : 1u, proto_sample.count()); |
| + EXPECT_EQ(duplicate_counts[i], proto_sample.count()); |
| for (size_t j = 0; j < arraysize(sample_frames[i]); ++j) { |
| SCOPED_TRACE("frame " + base::SizeTToString(j)); |
| const CallStackProfile::Entry& entry = proto_sample.entry().Get(j); |
| @@ -362,6 +506,19 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) { |
| EXPECT_EQ(sample_frames[i][j].module_index, |
| static_cast<size_t>(entry.module_id_index())); |
| } |
| + |
| + for (int x = 0; x < proto_sample.process_phase_size(); ++x) { |
| + process_phase |= 1 << proto_sample.process_phase(x); |
| + } |
| + EXPECT_EQ(profile_phases_and_activities[i][0], process_phase); |
| + |
| + for (int x = 0; x < proto_sample.activity_begun_size(); ++x) { |
| + current_activities |= 1 << proto_sample.activity_begun(x); |
| + } |
| + for (int x = 0; x < proto_sample.activity_ended_size(); ++x) { |
| + current_activities &= ~(1 << proto_sample.activity_ended(x)); |
| + } |
| + EXPECT_EQ(profile_phases_and_activities[i][1], current_activities); |
| } |
| } |
| @@ -382,12 +539,32 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) { |
| ) |
| }; |
| - // Duplicate samples in slots 0, 2, and 3. |
| + // Samples are unique only if the phase, activity, and frame all match. |
| + const uint32_t profile_phases_and_activities[][2] = { |
| + { 1, 0 }, |
| + { 1, 0 }, |
| + { 1, 0 }, |
| + { 1, 0 }, |
| + |
| + { 3, 0 }, |
| + { 3, 1 }, |
| + { 3, 0 }, |
| + { 3, 0 }, |
| + }; |
| const Frame sample_frames[][1] = { |
| { Frame(module_base_address + 0x10, 0), }, |
| { Frame(module_base_address + 0x20, 0), }, |
| { Frame(module_base_address + 0x10, 0), }, |
| - { Frame(module_base_address + 0x10, 0) } |
| + { Frame(module_base_address + 0x10, 0), }, |
| + |
| + { Frame(module_base_address + 0x10, 0), }, |
| + { Frame(module_base_address + 0x20, 0), }, |
| + { Frame(module_base_address + 0x10, 0), }, |
| + { Frame(module_base_address + 0x10, 0), }, |
| + }; |
| + |
| + const int duplicate_counts[] = { |
| + 1, 1, 2, 1, 1, 2 |
| }; |
| Profile profile; |
| @@ -397,8 +574,10 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) { |
| for (size_t i = 0; i < arraysize(sample_frames); ++i) { |
| profile.samples.push_back(Sample()); |
| Sample& sample = profile.samples.back(); |
| - sample.insert(sample.end(), &sample_frames[i][0], |
| - &sample_frames[i][0] + arraysize(sample_frames[i])); |
| + sample.process_phases = profile_phases_and_activities[i][0]; |
| + sample.current_activities = profile_phases_and_activities[i][1]; |
| + sample.frames.insert(sample.frames.end(), &sample_frames[i][0], |
| + &sample_frames[i][0] + arraysize(sample_frames[i])); |
| } |
| profile.profile_duration = base::TimeDelta::FromMilliseconds(100); |
| @@ -422,41 +601,57 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) { |
| const CallStackProfile& call_stack_profile = |
| sampled_profile.call_stack_profile(); |
| - ASSERT_EQ(3, call_stack_profile.sample().size()); |
| - for (int i = 0; i < 3; ++i) { |
| - SCOPED_TRACE("sample " + base::IntToString(i)); |
| + // For PRESERVE_ORDER, these are reset with every profile. |
| + uint32_t process_phase = 0; |
| + uint32_t current_activities = 0; |
| + |
| + ASSERT_EQ(static_cast<int>(arraysize(duplicate_counts)), |
| + call_stack_profile.sample().size()); |
| + for (size_t i = 0, d = 0; i < arraysize(duplicate_counts); |
| + d += duplicate_counts[i], ++i) { |
| + // |d| is the index after compensating for duplicate compression. |
| + SCOPED_TRACE("sample " + base::SizeTToString(i)); |
| const CallStackProfile::Sample& proto_sample = |
| call_stack_profile.sample().Get(i); |
| ASSERT_EQ(static_cast<int>(arraysize(sample_frames[i])), |
| proto_sample.entry().size()); |
| ASSERT_TRUE(proto_sample.has_count()); |
| - EXPECT_EQ(i == 2 ? 2u : 1u, proto_sample.count()); |
| - for (size_t j = 0; j < arraysize(sample_frames[i]); ++j) { |
| + EXPECT_EQ(duplicate_counts[i], proto_sample.count()); |
| + for (size_t j = 0; j < arraysize(sample_frames[d]); ++j) { |
| SCOPED_TRACE("frame " + base::SizeTToString(j)); |
| const CallStackProfile::Entry& entry = proto_sample.entry().Get(j); |
| ASSERT_TRUE(entry.has_address()); |
| const char* instruction_pointer = reinterpret_cast<const char*>( |
| - sample_frames[i][j].instruction_pointer); |
| + sample_frames[d][j].instruction_pointer); |
| const char* module_base_address = reinterpret_cast<const char*>( |
| - modules[sample_frames[i][j].module_index].base_address); |
| + modules[sample_frames[d][j].module_index].base_address); |
| EXPECT_EQ( |
| static_cast<uint64_t>(instruction_pointer - module_base_address), |
| entry.address()); |
| ASSERT_TRUE(entry.has_module_id_index()); |
| - EXPECT_EQ(sample_frames[i][j].module_index, |
| + EXPECT_EQ(sample_frames[d][j].module_index, |
| static_cast<size_t>(entry.module_id_index())); |
| } |
| + |
| + for (int x = 0; x < proto_sample.process_phase_size(); ++x) { |
| + process_phase |= 1 << proto_sample.process_phase(x); |
| + } |
| + EXPECT_EQ(profile_phases_and_activities[d][0], process_phase); |
| + |
| + for (int x = 0; x < proto_sample.activity_begun_size(); ++x) { |
| + current_activities |= 1 << proto_sample.activity_begun(x); |
| + } |
| + for (int x = 0; x < proto_sample.activity_ended_size(); ++x) { |
| + current_activities &= ~(1 << proto_sample.activity_ended(x)); |
| + } |
| + EXPECT_EQ(profile_phases_and_activities[d][1], current_activities); |
| } |
| } |
| // Checks that unknown modules produce an empty Entry. |
| TEST_F(CallStackProfileMetricsProviderTest, UnknownModule) { |
| - const Frame frame(0x1000, Frame::kUnknownModuleIndex); |
| - |
| Profile profile; |
| - |
| - profile.samples.push_back(Sample(1, frame)); |
| - |
| + profile.samples.push_back(Sample(Frame(0x1000, Frame::kUnknownModuleIndex))); |
| profile.profile_duration = base::TimeDelta::FromMilliseconds(100); |
| profile.sampling_period = base::TimeDelta::FromMilliseconds(10); |
| Profiles profiles; |
| @@ -497,7 +692,7 @@ TEST_F(CallStackProfileMetricsProviderTest, ProfilesProvidedOnlyOnce) { |
| for (int i = 0; i < 2; ++i) { |
| Profile profile; |
| profile.samples.push_back( |
| - Sample(1, Frame(0x1000, Frame::kUnknownModuleIndex))); |
| + Sample((Frame(0x1000, Frame::kUnknownModuleIndex)))); |
| profile.profile_duration = base::TimeDelta::FromMilliseconds(100); |
| // Use the sampling period to distinguish the two profiles. |
| @@ -530,9 +725,7 @@ TEST_F(CallStackProfileMetricsProviderTest, ProfilesProvidedOnlyOnce) { |
| TEST_F(CallStackProfileMetricsProviderTest, |
| ProfilesProvidedWhenCollectedBeforeInstantiation) { |
| Profile profile; |
| - profile.samples.push_back( |
| - Sample(1, Frame(0x1000, Frame::kUnknownModuleIndex))); |
| - |
| + profile.samples.push_back(Sample(Frame(0x1000, Frame::kUnknownModuleIndex))); |
| profile.profile_duration = base::TimeDelta::FromMilliseconds(100); |
| profile.sampling_period = base::TimeDelta::FromMilliseconds(10); |
| Profiles profiles; |
| @@ -557,9 +750,7 @@ TEST_F(CallStackProfileMetricsProviderTest, |
| // while recording is disabled. |
| TEST_F(CallStackProfileMetricsProviderTest, ProfilesNotProvidedWhileDisabled) { |
| Profile profile; |
| - profile.samples.push_back( |
| - Sample(1, Frame(0x1000, Frame::kUnknownModuleIndex))); |
| - |
| + profile.samples.push_back(Sample(Frame(0x1000, Frame::kUnknownModuleIndex))); |
| profile.profile_duration = base::TimeDelta::FromMilliseconds(100); |
| profile.sampling_period = base::TimeDelta::FromMilliseconds(10); |
| Profiles profiles; |
| @@ -584,9 +775,7 @@ TEST_F(CallStackProfileMetricsProviderTest, ProfilesNotProvidedWhileDisabled) { |
| TEST_F(CallStackProfileMetricsProviderTest, |
| ProfilesNotProvidedAfterChangeToDisabled) { |
| Profile profile; |
| - profile.samples.push_back( |
| - Sample(1, Frame(0x1000, Frame::kUnknownModuleIndex))); |
| - |
| + profile.samples.push_back(Sample(Frame(0x1000, Frame::kUnknownModuleIndex))); |
| profile.profile_duration = base::TimeDelta::FromMilliseconds(100); |
| profile.sampling_period = base::TimeDelta::FromMilliseconds(10); |
| @@ -614,9 +803,7 @@ TEST_F(CallStackProfileMetricsProviderTest, |
| TEST_F(CallStackProfileMetricsProviderTest, |
| ProfilesNotProvidedAfterChangeToDisabledThenEnabled) { |
| Profile profile; |
| - profile.samples.push_back( |
| - Sample(1, Frame(0x1000, Frame::kUnknownModuleIndex))); |
| - |
| + profile.samples.push_back(Sample(Frame(0x1000, Frame::kUnknownModuleIndex))); |
| profile.profile_duration = base::TimeDelta::FromMilliseconds(100); |
| profile.sampling_period = base::TimeDelta::FromMilliseconds(10); |
| @@ -645,9 +832,7 @@ TEST_F(CallStackProfileMetricsProviderTest, |
| TEST_F(CallStackProfileMetricsProviderTest, |
| ProfilesNotProvidedAfterChangeFromDisabled) { |
| Profile profile; |
| - profile.samples.push_back( |
| - Sample(1, Frame(0x1000, Frame::kUnknownModuleIndex))); |
| - |
| + profile.samples.push_back(Sample(Frame(0x1000, Frame::kUnknownModuleIndex))); |
| profile.profile_duration = base::TimeDelta::FromMilliseconds(100); |
| profile.sampling_period = base::TimeDelta::FromMilliseconds(10); |