Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(134)

Unified Diff: components/metrics/call_stack_profile_metrics_provider_unittest.cc

Issue 981143006: Metrics provider for statistical stack profiler (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@lkcr
Patch Set: handle unknown module (per other review) Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
new file mode 100644
index 0000000000000000000000000000000000000000..0552a3d6ad87b2e9ad423a0ce9784f1cfe67e630
--- /dev/null
+++ b/components/metrics/call_stack_profile_metrics_provider_unittest.cc
@@ -0,0 +1,414 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/metrics/call_stack_profile_metrics_provider.h"
+
+#include "base/profiler/stack_sampling_profiler.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StackSamplingProfiler;
+using Frame = StackSamplingProfiler::Frame;
+using Module = StackSamplingProfiler::Module;
+using Profile = StackSamplingProfiler::Profile;
+using Sample = StackSamplingProfiler::Sample;
+
+namespace metrics {
+
+// Checks that all properties from multiple profiles are filled as expected.
+TEST(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] = {
+ {
+ {
+ reinterpret_cast<const void*>(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
+ },
+ {
+ reinterpret_cast<const void*>(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
+ }
+ },
+ {
+ {
+ reinterpret_cast<const void*>(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
+ },
+ { // Repeated from the first profile.
+ reinterpret_cast<const void*>(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
+ },
+ }
+ };
+
+ // Values for Windows generated with:
+ // perl -MDigest::MD5=md5 -MEncode=encode \
+ // -e 'for(@ARGV){printf "%x\n", unpack "Q>", md5 encode "UTF-16LE", $_}' \
+ // chrome.exe third_party.dll third_party2.dll
+ //
+ // Values for Linux generated with:
+ // perl -MDigest::MD5=md5 \
+ // -e 'for(@ARGV){printf "%x\n", unpack "Q>", md5 $_}' \
+ // chrome third_party.so third_party2.so
+ const uint64 profile_expected_name_md5_prefixes[][2] = {
+ {
+#if defined(OS_WIN)
+ 0x46c3e4166659ac02ULL,
+ 0x7e2b8bfddeae1abaULL
+#else
+ 0x554838a8451ac36cUL,
+ 0x843661148659c9f8UL
+#endif
+ },
+ {
+#if defined(OS_WIN)
+ 0x87b66f4573a4d5caULL,
+ 0x46c3e4166659ac02ULL
+#else
+ 0xb4647e539fa6ec9eUL,
+ 0x554838a8451ac36cUL
+#endif
+ }
+ };
+
+ // 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.
+ const Frame profile_sample_frames[][2][3] = {
+ {
+ {
+ { reinterpret_cast<const void*>(module1_base_address + 0x10), 0 },
+ { reinterpret_cast<const void*>(module2_base_address + 0x20), 1 },
+ { reinterpret_cast<const void*>(module1_base_address + 0x30), 0 }
+ },
+ {
+ { reinterpret_cast<const void*>(module2_base_address + 0x10), 1 },
+ { reinterpret_cast<const void*>(module1_base_address + 0x20), 0 },
+ { reinterpret_cast<const void*>(module2_base_address + 0x30), 1 }
+ }
+ },
+ {
+ {
+ { reinterpret_cast<const void*>(module3_base_address + 0x10), 0 },
+ { reinterpret_cast<const void*>(module1_base_address + 0x20), 1 },
+ { reinterpret_cast<const void*>(module3_base_address + 0x30), 0 }
+ },
+ {
+ { reinterpret_cast<const void*>(module1_base_address + 0x10), 1 },
+ { reinterpret_cast<const void*>(module3_base_address + 0x20), 0 },
+ { reinterpret_cast<const void*>(module1_base_address + 0x30), 1 }
+ }
+ }
+ };
+
+ base::TimeDelta profile_durations[2] = {
+ base::TimeDelta::FromMilliseconds(100),
+ base::TimeDelta::FromMilliseconds(200)
+ };
+
+ base::TimeDelta profile_sampling_periods[2] = {
+ base::TimeDelta::FromMilliseconds(10),
+ base::TimeDelta::FromMilliseconds(20)
+ };
+
+ 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];
+ profile.preserve_sample_ordering = false;
+
+ profiles.push_back(profile);
+ }
+
+ CallStackProfileMetricsProvider provider;
+ provider.SetSourceProfilesForTesting(profiles);
+ metrics::ChromeUserMetricsExtension uma_proto;
+ provider.ProvideGeneralMetrics(&uma_proto);
+
+ ASSERT_EQ(static_cast<int>(arraysize(profile_sample_frames)),
+ uma_proto.sampled_profile().size());
+ for (size_t i = 0; i < arraysize(profile_sample_frames); ++i) {
+ SCOPED_TRACE("profile " + base::IntToString(i));
+ const metrics::SampledProfile& sampled_profile =
+ uma_proto.sampled_profile().Get(i);
+ ASSERT_TRUE(sampled_profile.has_call_stack_profile());
+ const metrics::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::IntToString(j));
+ const metrics::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::IntToString(k));
+ const metrics::CallStackEntry& 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>(instruction_pointer -
+ module_base_address), entry.address());
+ ASSERT_TRUE(entry.has_module_id_index());
+ EXPECT_EQ(profile_sample_frames[i][j][k].module_index,
+ 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::IntToString(j));
+ const metrics::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());
+ }
+}
+
+// Checks that all duplicate samples are collapsed with
+// preserve_sample_ordering = false.
+TEST(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) {
+ const uintptr_t module_base_address = 0x1000;
+
+ const Module modules[] = {
+ {
+ reinterpret_cast<const void*>(module_base_address),
+ "ABCD",
+#if defined(OS_WIN)
+ base::FilePath(L"c:\\some\\path\\to\\chrome.exe")
+#else
+ base::FilePath("/some/path/to/chrome")
+#endif
+ }
+ };
+
+ // Duplicate samples in slots 0, 2, and 3.
+ const Frame sample_frames[][1] = {
+ { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, },
+ { { reinterpret_cast<const void*>(module_base_address + 0x20), 0 }, },
+ { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, },
+ { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, },
+ };
+
+ Profile profile;
+ profile.modules.insert(profile.modules.end(), &modules[0],
+ &modules[0] + arraysize(modules));
+
+ 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]));
+ }
+
+ profile.profile_duration = base::TimeDelta::FromMilliseconds(100);
+ profile.sampling_period = base::TimeDelta::FromMilliseconds(10);
+ profile.preserve_sample_ordering = false;
+
+ CallStackProfileMetricsProvider provider;
+ provider.SetSourceProfilesForTesting(std::vector<Profile>(1, profile));
+ metrics::ChromeUserMetricsExtension uma_proto;
+ provider.ProvideGeneralMetrics(&uma_proto);
+
+ ASSERT_EQ(1, uma_proto.sampled_profile().size());
+ const metrics::SampledProfile& sampled_profile =
+ uma_proto.sampled_profile().Get(0);
+ ASSERT_TRUE(sampled_profile.has_call_stack_profile());
+ const metrics::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));
+ const metrics::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 == 0 ? 3u : 1u, proto_sample.count());
+ for (size_t j = 0; j < arraysize(sample_frames[i]); ++j) {
+ SCOPED_TRACE("frame " + base::IntToString(j));
+ const metrics::CallStackEntry& 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);
+ const char* module_base_address = reinterpret_cast<const char*>(
+ modules[sample_frames[i][j].module_index].base_address);
+ EXPECT_EQ(static_cast<uint64>(instruction_pointer - module_base_address),
+ entry.address());
+ ASSERT_TRUE(entry.has_module_id_index());
+ EXPECT_EQ(sample_frames[i][j].module_index, entry.module_id_index());
+ }
+ }
+}
+
+// Checks that only contiguous duplicate samples are collapsed with
+// preserve_sample_ordering = true.
+TEST(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) {
+ const uintptr_t module_base_address = 0x1000;
+
+ const Module modules[] = {
+ {
+ reinterpret_cast<const void*>(module_base_address),
+ "ABCD",
+#if defined(OS_WIN)
+ base::FilePath(L"c:\\some\\path\\to\\chrome.exe")
+#else
+ base::FilePath("/some/path/to/chrome")
+#endif
+ }
+ };
+
+ // Duplicate samples in slots 0, 2, and 3.
+ const Frame sample_frames[][1] = {
+ { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, },
+ { { reinterpret_cast<const void*>(module_base_address + 0x20), 0 }, },
+ { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, },
+ { { reinterpret_cast<const void*>(module_base_address + 0x10), 0 }, }
+ };
+
+ Profile profile;
+ profile.modules.insert(profile.modules.end(), &modules[0],
+ &modules[0] + arraysize(modules));
+
+ 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]));
+ }
+
+ profile.profile_duration = base::TimeDelta::FromMilliseconds(100);
+ profile.sampling_period = base::TimeDelta::FromMilliseconds(10);
+ profile.preserve_sample_ordering = true;
+
+ CallStackProfileMetricsProvider provider;
+ provider.SetSourceProfilesForTesting(std::vector<Profile>(1, profile));
+ metrics::ChromeUserMetricsExtension uma_proto;
+ provider.ProvideGeneralMetrics(&uma_proto);
+
+ ASSERT_EQ(1, uma_proto.sampled_profile().size());
+ const metrics::SampledProfile& sampled_profile =
+ uma_proto.sampled_profile().Get(0);
+ ASSERT_TRUE(sampled_profile.has_call_stack_profile());
+ const metrics::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));
+ const metrics::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) {
+ SCOPED_TRACE("frame " + base::IntToString(j));
+ const metrics::CallStackEntry& 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);
+ const char* module_base_address = reinterpret_cast<const char*>(
+ modules[sample_frames[i][j].module_index].base_address);
+ EXPECT_EQ(static_cast<uint64>(instruction_pointer - module_base_address),
+ entry.address());
+ ASSERT_TRUE(entry.has_module_id_index());
+ EXPECT_EQ(sample_frames[i][j].module_index, entry.module_id_index());
+ }
+ }
+}
+
+
+// Checks that unknown modules produce an empty CallStackEntry.
+TEST(CallStackProfileMetricsProviderTest, UnknownModule) {
+ // -1 indicates an unknown module.
+ const Frame frame = { reinterpret_cast<const void*>(0x1000), -1 };
+
+ Profile profile;
+
+ profile.samples.push_back(Sample(1, frame));
+
+ profile.profile_duration = base::TimeDelta::FromMilliseconds(100);
+ profile.sampling_period = base::TimeDelta::FromMilliseconds(10);
+ profile.preserve_sample_ordering = false;
+
+ CallStackProfileMetricsProvider provider;
+ provider.SetSourceProfilesForTesting(std::vector<Profile>(1, profile));
+ metrics::ChromeUserMetricsExtension uma_proto;
+ provider.ProvideGeneralMetrics(&uma_proto);
+
+ ASSERT_EQ(1, uma_proto.sampled_profile().size());
+ const metrics::SampledProfile& sampled_profile =
+ uma_proto.sampled_profile().Get(0);
+ ASSERT_TRUE(sampled_profile.has_call_stack_profile());
+ const metrics::CallStackProfile& call_stack_profile =
+ sampled_profile.call_stack_profile();
+
+ ASSERT_EQ(1u, call_stack_profile.sample().size());
+ const metrics::CallStackProfile::Sample& proto_sample =
+ call_stack_profile.sample().Get(0);
+ ASSERT_EQ(1u, proto_sample.entry().size());
+ ASSERT_TRUE(proto_sample.has_count());
+ EXPECT_EQ(1u, proto_sample.count());
+ const metrics::CallStackEntry& entry = proto_sample.entry().Get(0);
+ EXPECT_FALSE(entry.has_address());
+ EXPECT_FALSE(entry.has_module_id_index());
+}
+
+} // namespace metrics

Powered by Google App Engine
This is Rietveld 408576698