diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-01-04 14:17:57 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-01-05 10:05:06 +0000 |
commit | 39d357e3248f80abea0159765ff39554affb40db (patch) | |
tree | aba0e6bfb76de0244bba0f5fdbd64b830dd6e621 /chromium/components/metrics | |
parent | 87778abf5a1f89266f37d1321b92a21851d8244d (diff) | |
download | qtwebengine-chromium-39d357e3248f80abea0159765ff39554affb40db.tar.gz |
BASELINE: Update Chromium to 55.0.2883.105
And updates ninja to 1.7.2
Change-Id: I20d43c737f82764d857ada9a55586901b18b9243
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/components/metrics')
71 files changed, 3080 insertions, 455 deletions
diff --git a/chromium/components/metrics/BUILD.gn b/chromium/components/metrics/BUILD.gn index 7fc0934af87..5c10219cdec 100644 --- a/chromium/components/metrics/BUILD.gn +++ b/chromium/components/metrics/BUILD.gn @@ -13,8 +13,7 @@ if (metrics_use_blimp) { defines = [ "OVERRIDE_OS_NAME_TO_BLIMP" ] } -# GYP version: components/metrics.gypi:metrics -source_set("metrics") { +static_library("metrics") { sources = [ "call_stack_profile_metrics_provider.cc", "call_stack_profile_metrics_provider.h", @@ -83,6 +82,7 @@ source_set("metrics") { "//components/metrics/proto", ] deps = [ + ":call_stack_profile_params", "//base", "//base:base_static", "//components/prefs", @@ -110,8 +110,7 @@ source_set("metrics") { } if (!is_ios) { - # GYP version: components/metrics.gypi:metrics_gpu - source_set("gpu") { + static_library("gpu") { sources = [ "gpu/gpu_metrics_provider.cc", "gpu/gpu_metrics_provider.h", @@ -128,36 +127,6 @@ if (!is_ios) { } } -if (is_chromeos) { - # GYP version: components/metrics.gypi:metrics_leak_detector - source_set("leak_detector") { - sources = [ - "leak_detector/call_stack_manager.cc", - "leak_detector/call_stack_manager.h", - "leak_detector/call_stack_table.cc", - "leak_detector/call_stack_table.h", - "leak_detector/custom_allocator.cc", - "leak_detector/custom_allocator.h", - "leak_detector/leak_analyzer.cc", - "leak_detector/leak_analyzer.h", - "leak_detector/leak_detector.cc", - "leak_detector/leak_detector.h", - "leak_detector/leak_detector_impl.cc", - "leak_detector/leak_detector_impl.h", - "leak_detector/leak_detector_value_type.cc", - "leak_detector/leak_detector_value_type.h", - "leak_detector/ranked_set.cc", - "leak_detector/ranked_set.h", - ] - - deps = [ - "//base", - "//components/metrics/proto:proto", - ] - } -} - -# GYP version: components/metrics.gypi:metrics_net static_library("net") { sources = [ "net/cellular_logic_helper.cc", @@ -195,8 +164,7 @@ static_library("net") { } } -# GYP version: components/metrics.gypi:metrics_profiler -source_set("profiler") { +static_library("profiler") { sources = [ "profiler/profiler_metrics_provider.cc", "profiler/profiler_metrics_provider.h", @@ -216,8 +184,7 @@ source_set("profiler") { ] } -# GYP version: components/metrics.gypi:metrics_ui -source_set("ui") { +static_library("ui") { sources = [ "ui/screen_info_metrics_provider.cc", "ui/screen_info_metrics_provider.h", @@ -235,8 +202,7 @@ source_set("ui") { } if (!is_ios) { - # GYP version: components/metrics.gypi:metrics_profiler_content - source_set("profiler_content") { + static_library("profiler_content") { sources = [ "profiler/content/content_tracking_synchronizer_delegate.cc", "profiler/content/content_tracking_synchronizer_delegate.h", @@ -253,8 +219,7 @@ if (!is_ios) { ] } } else { - # GYP version: components/metrics.gypi:metrics_profiler_ios - source_set("profiler_ios") { + static_library("profiler_ios") { sources = [ "profiler/ios/ios_tracking_synchronizer_delegate.cc", "profiler/ios/ios_tracking_synchronizer_delegate.h", @@ -269,8 +234,36 @@ if (!is_ios) { } } -# GYP version: components/metrics.gypi:metrics_test_support -source_set("test_support") { +source_set("call_stack_profile_params") { + sources = [ + "call_stack_profile_params.cc", + "call_stack_profile_params.h", + ] +} + +source_set("call_stacks") { + sources = [ + "call_stack_profile_collector.cc", + "call_stack_profile_collector.h", + ] + deps = [ + ":metrics", + "//components/metrics/public/interfaces:call_stack_mojo_bindings", + ] +} + +source_set("child_call_stacks") { + sources = [ + "child_call_stack_profile_collector.cc", + "child_call_stack_profile_collector.h", + ] + deps = [ + "//components/metrics/public/interfaces:call_stack_mojo_bindings", + "//services/shell/public/cpp", + ] +} + +static_library("test_support") { testonly = true sources = [ "test_enabled_state_provider.cc", @@ -290,8 +283,7 @@ source_set("test_support") { } if (is_linux) { - # GYP version: components/metrics.gypi:metrics_serialization - source_set("serialization") { + static_library("serialization") { sources = [ "serialization/metric_sample.cc", "serialization/metric_sample.h", @@ -304,32 +296,11 @@ if (is_linux) { } } -if (is_chromeos) { - source_set("leak_detector_unit_tests") { - testonly = true - sources = [ - "leak_detector/call_stack_manager_unittest.cc", - "leak_detector/call_stack_table_unittest.cc", - "leak_detector/leak_analyzer_unittest.cc", - "leak_detector/leak_detector_impl_unittest.cc", - "leak_detector/leak_detector_unittest.cc", - "leak_detector/ranked_set_unittest.cc", - ] - - deps = [ - ":leak_detector", - "//base", - "//components/metrics/proto:proto", - "//content/test:test_support", - "//testing/gtest", - ] - } -} - source_set("unit_tests") { testonly = true sources = [ "call_stack_profile_metrics_provider_unittest.cc", + "child_call_stack_profile_collector_unittest.cc", "cloned_install_detector_unittest.cc", "daily_event_unittest.cc", "data_use_tracker_unittest.cc", @@ -351,15 +322,20 @@ source_set("unit_tests") { ] deps = [ + ":call_stack_profile_params", + ":child_call_stacks", ":metrics", ":net", ":profiler", ":test_support", ":ui", "//base/test:test_support", + "//components/metrics/public/cpp:call_stack_unit_tests", "//components/prefs:test_support", "//components/variations", + "//mojo/public/cpp/bindings", "//net:test_support", + "//services/shell/public/cpp:sources", "//testing/gtest", "//third_party/zlib:compression_utils", "//ui/gfx/geometry", @@ -371,6 +347,13 @@ source_set("unit_tests") { } if (is_chromeos) { - deps += [ ":leak_detector_unit_tests" ] + deps += [ "leak_detector:unit_tests" ] + } + + # iOS is not supported by the profiler and the ios-simulator bot chokes on + # these tests. + if (is_ios) { + sources -= [ "child_call_stack_profile_collector_unittest.cc" ] + deps -= [ "//components/metrics/public/cpp:call_stack_unit_tests" ] } } diff --git a/chromium/components/metrics/DEPS b/chromium/components/metrics/DEPS index 030dd191a6a..a7bf8dc8799 100644 --- a/chromium/components/metrics/DEPS +++ b/chromium/components/metrics/DEPS @@ -8,6 +8,8 @@ include_rules = [ "+components/variations", "+components/version_info", "+content/public/test", + "+mojo/public/cpp", + "+services/shell/public/cpp", "+third_party/zlib/google", "-net", ] diff --git a/chromium/components/metrics/call_stack_profile_collector.cc b/chromium/components/metrics/call_stack_profile_collector.cc new file mode 100644 index 00000000000..cbf466f859a --- /dev/null +++ b/chromium/components/metrics/call_stack_profile_collector.cc @@ -0,0 +1,41 @@ +// Copyright 2016 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_collector.h" + +#include "base/memory/ptr_util.h" +#include "components/metrics/call_stack_profile_metrics_provider.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/strong_binding.h" + +namespace metrics { + +CallStackProfileCollector::CallStackProfileCollector( + CallStackProfileParams::Process expected_process) + : expected_process_(expected_process) {} + +CallStackProfileCollector::~CallStackProfileCollector() {} + +// static +void CallStackProfileCollector::Create( + CallStackProfileParams::Process expected_process, + mojom::CallStackProfileCollectorRequest request) { + mojo::MakeStrongBinding( + base::MakeUnique<CallStackProfileCollector>(expected_process), + std::move(request)); +} + +void CallStackProfileCollector::Collect( + const CallStackProfileParams& params, + base::TimeTicks start_timestamp, + const std::vector<CallStackProfile>& profiles) { + if (params.process != expected_process_) + return; + + CallStackProfileMetricsProvider::ReceiveCompletedProfiles(params, + start_timestamp, + profiles); +} + +} // namespace metrics diff --git a/chromium/components/metrics/call_stack_profile_collector.h b/chromium/components/metrics/call_stack_profile_collector.h new file mode 100644 index 00000000000..f6a2ca1ce53 --- /dev/null +++ b/chromium/components/metrics/call_stack_profile_collector.h @@ -0,0 +1,41 @@ +// Copyright 2016 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. + +#ifndef COMPONENTS_METRICS_CALL_STACK_PROFILE_COLLECTOR_H_ +#define COMPONENTS_METRICS_CALL_STACK_PROFILE_COLLECTOR_H_ + +#include "base/macros.h" +#include "components/metrics/public/interfaces/call_stack_profile_collector.mojom.h" + +namespace metrics { + +class CallStackProfileCollector : public mojom::CallStackProfileCollector { + public: + using CallStackProfile = base::StackSamplingProfiler::CallStackProfile; + + explicit CallStackProfileCollector( + CallStackProfileParams::Process expected_process); + ~CallStackProfileCollector() override; + + // Create a collector to receive profiles from |expected_process|. + static void Create(CallStackProfileParams::Process expected_process, + mojom::CallStackProfileCollectorRequest request); + + // mojom::CallStackProfileCollector: + void Collect(const CallStackProfileParams& params, + base::TimeTicks start_timestamp, + const std::vector<CallStackProfile>& profiles) override; + + private: + // Profile params are validated to come from this process. Profiles with a + // different process declared in the params are considered untrustworthy and + // ignored. + const CallStackProfileParams::Process expected_process_; + + DISALLOW_COPY_AND_ASSIGN(CallStackProfileCollector); +}; + +} // namespace metrics + +#endif // COMPONENTS_METRICS_CALL_STACK_PROFILE_COLLECTOR_H_ diff --git a/chromium/components/metrics/call_stack_profile_metrics_provider.cc b/chromium/components/metrics/call_stack_profile_metrics_provider.cc index 3b0b3279411..3dc55b9ea6d 100644 --- a/chromium/components/metrics/call_stack_profile_metrics_provider.cc +++ b/chromium/components/metrics/call_stack_profile_metrics_provider.cc @@ -38,13 +38,13 @@ namespace { // A set of profiles and the CallStackProfileMetricsProvider state associated // with them. struct ProfilesState { - ProfilesState(const CallStackProfileMetricsProvider::Params& params, + ProfilesState(const CallStackProfileParams& params, const base::StackSamplingProfiler::CallStackProfiles& profiles, base::TimeTicks start_timestamp); // The metrics-related parameters provided to // CallStackProfileMetricsProvider::GetProfilerCallback(). - CallStackProfileMetricsProvider::Params params; + CallStackProfileParams params; // The call stack profiles collected by the profiler. base::StackSamplingProfiler::CallStackProfiles profiles; @@ -57,7 +57,7 @@ struct ProfilesState { }; ProfilesState::ProfilesState( - const CallStackProfileMetricsProvider::Params& params, + const CallStackProfileParams& params, const base::StackSamplingProfiler::CallStackProfiles& profiles, base::TimeTicks start_timestamp) : params(params), @@ -186,10 +186,10 @@ PendingProfiles::~PendingProfiles() {} // Functions to process completed profiles ------------------------------------ -// Invoked on the profiler's thread. Provides the profiles to PendingProfiles to -// append, if the collecting state allows. -void ReceiveCompletedProfiles( - const CallStackProfileMetricsProvider::Params& params, +// Will be invoked on either the main thread or the profiler's thread. Provides +// the profiles to PendingProfiles to append, if the collecting state allows. +void ReceiveCompletedProfilesImpl( + const CallStackProfileParams& params, base::TimeTicks start_timestamp, const StackSamplingProfiler::CallStackProfiles& profiles) { PendingProfiles::GetInstance()->CollectProfilesIfCollectionEnabled( @@ -239,12 +239,12 @@ void CopySampleToProto( // Transcode |profile| into |proto_profile|. void CopyProfileToProto( const StackSamplingProfiler::CallStackProfile& profile, - bool preserve_sample_ordering, + CallStackProfileParams::SampleOrderingSpec ordering_spec, CallStackProfile* proto_profile) { if (profile.samples.empty()) return; - if (preserve_sample_ordering) { + if (ordering_spec == CallStackProfileParams::PRESERVE_ORDER) { // Collapse only consecutive repeated samples together. CallStackProfile::Sample* current_sample_proto = nullptr; for (auto it = profile.samples.begin(); it != profile.samples.end(); ++it) { @@ -289,23 +289,77 @@ void CopyProfileToProto( profile.sampling_period.InMilliseconds()); } -// Translates CallStackProfileMetricsProvider's trigger to the corresponding +// Translates CallStackProfileParams's process to the corresponding +// execution context Process. +Process ToExecutionContextProcess(CallStackProfileParams::Process process) { + switch (process) { + case CallStackProfileParams::UNKNOWN_PROCESS: + return UNKNOWN_PROCESS; + case CallStackProfileParams::BROWSER_PROCESS: + return BROWSER_PROCESS; + case CallStackProfileParams::RENDERER_PROCESS: + return RENDERER_PROCESS; + case CallStackProfileParams::GPU_PROCESS: + return GPU_PROCESS; + case CallStackProfileParams::UTILITY_PROCESS: + return UTILITY_PROCESS; + case CallStackProfileParams::ZYGOTE_PROCESS: + return ZYGOTE_PROCESS; + case CallStackProfileParams::SANDBOX_HELPER_PROCESS: + return SANDBOX_HELPER_PROCESS; + case CallStackProfileParams::PPAPI_PLUGIN_PROCESS: + return PPAPI_PLUGIN_PROCESS; + case CallStackProfileParams::PPAPI_BROKER_PROCESS: + return PPAPI_BROKER_PROCESS; + } + NOTREACHED(); + return UNKNOWN_PROCESS; +} + +// Translates CallStackProfileParams's thread to the corresponding +// SampledProfile TriggerEvent. +Thread ToExecutionContextThread(CallStackProfileParams::Thread thread) { + switch (thread) { + case CallStackProfileParams::UNKNOWN_THREAD: + return UNKNOWN_THREAD; + case CallStackProfileParams::UI_THREAD: + return UI_THREAD; + case CallStackProfileParams::FILE_THREAD: + return FILE_THREAD; + case CallStackProfileParams::FILE_USER_BLOCKING_THREAD: + return FILE_USER_BLOCKING_THREAD; + case CallStackProfileParams::PROCESS_LAUNCHER_THREAD: + return PROCESS_LAUNCHER_THREAD; + case CallStackProfileParams::CACHE_THREAD: + return CACHE_THREAD; + case CallStackProfileParams::IO_THREAD: + return IO_THREAD; + case CallStackProfileParams::DB_THREAD: + return DB_THREAD; + case CallStackProfileParams::GPU_MAIN_THREAD: + return GPU_MAIN_THREAD; + case CallStackProfileParams::RENDER_THREAD: + return RENDER_THREAD; + case CallStackProfileParams::UTILITY_THREAD: + return UTILITY_THREAD; + } + NOTREACHED(); + return UNKNOWN_THREAD; +} + +// Translates CallStackProfileParams's trigger to the corresponding // SampledProfile TriggerEvent. SampledProfile::TriggerEvent ToSampledProfileTriggerEvent( - CallStackProfileMetricsProvider::Trigger trigger) { + CallStackProfileParams::Trigger trigger) { switch (trigger) { - case CallStackProfileMetricsProvider::UNKNOWN: + case CallStackProfileParams::UNKNOWN: return SampledProfile::UNKNOWN_TRIGGER_EVENT; - break; - case CallStackProfileMetricsProvider::PROCESS_STARTUP: + case CallStackProfileParams::PROCESS_STARTUP: return SampledProfile::PROCESS_STARTUP; - break; - case CallStackProfileMetricsProvider::JANKY_TASK: + case CallStackProfileParams::JANKY_TASK: return SampledProfile::JANKY_TASK; - break; - case CallStackProfileMetricsProvider::THREAD_HUNG: + case CallStackProfileParams::THREAD_HUNG: return SampledProfile::THREAD_HUNG; - break; } NOTREACHED(); return SampledProfile::UNKNOWN_TRIGGER_EVENT; @@ -313,20 +367,6 @@ SampledProfile::TriggerEvent ToSampledProfileTriggerEvent( } // namespace -// CallStackProfileMetricsProvider::Params ------------------------------------ - -CallStackProfileMetricsProvider::Params::Params( - CallStackProfileMetricsProvider::Trigger trigger) - : Params(trigger, false) { -} - -CallStackProfileMetricsProvider::Params::Params( - CallStackProfileMetricsProvider::Trigger trigger, - bool preserve_sample_ordering) - : trigger(trigger), - preserve_sample_ordering(preserve_sample_ordering) { -} - // CallStackProfileMetricsProvider -------------------------------------------- const char CallStackProfileMetricsProvider::kFieldTrialName[] = @@ -342,14 +382,24 @@ CallStackProfileMetricsProvider::~CallStackProfileMetricsProvider() { // This function can be invoked on an abitrary thread. base::StackSamplingProfiler::CompletedCallback -CallStackProfileMetricsProvider::GetProfilerCallback(const Params& params) { +CallStackProfileMetricsProvider::GetProfilerCallback( + const CallStackProfileParams& params) { // Ignore the profiles if the collection is disabled. If the collection state // changes while collecting, this will be detected by the callback and // profiles will be ignored at that point. if (!PendingProfiles::GetInstance()->IsCollectionEnabled()) return base::Bind(&IgnoreCompletedProfiles); - return base::Bind(&ReceiveCompletedProfiles, params, base::TimeTicks::Now()); + return base::Bind(&ReceiveCompletedProfilesImpl, params, + base::TimeTicks::Now()); +} + +// static +void CallStackProfileMetricsProvider::ReceiveCompletedProfiles( + const CallStackProfileParams& params, + base::TimeTicks start_timestamp, + const base::StackSamplingProfiler::CallStackProfiles& profiles) { + ReceiveCompletedProfilesImpl(params, start_timestamp, profiles); } void CallStackProfileMetricsProvider::OnRecordingEnabled() { @@ -371,10 +421,13 @@ void CallStackProfileMetricsProvider::ProvideGeneralMetrics( for (const StackSamplingProfiler::CallStackProfile& profile : profiles_state.profiles) { SampledProfile* sampled_profile = uma_proto->add_sampled_profile(); + sampled_profile->set_process(ToExecutionContextProcess( + profiles_state.params.process)); + sampled_profile->set_thread(ToExecutionContextThread( + profiles_state.params.thread)); sampled_profile->set_trigger_event(ToSampledProfileTriggerEvent( profiles_state.params.trigger)); - CopyProfileToProto(profile, - profiles_state.params.preserve_sample_ordering, + CopyProfileToProto(profile, profiles_state.params.ordering_spec, sampled_profile->mutable_call_stack_profile()); } } diff --git a/chromium/components/metrics/call_stack_profile_metrics_provider.h b/chromium/components/metrics/call_stack_profile_metrics_provider.h index 746b82928ed..d26000be087 100644 --- a/chromium/components/metrics/call_stack_profile_metrics_provider.h +++ b/chromium/components/metrics/call_stack_profile_metrics_provider.h @@ -10,6 +10,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/profiler/stack_sampling_profiler.h" +#include "components/metrics/call_stack_profile_params.h" #include "components/metrics/metrics_provider.h" namespace metrics { @@ -18,34 +19,6 @@ class ChromeUserMetricsExtension; // Performs metrics logging for the stack sampling profiler. class CallStackProfileMetricsProvider : public MetricsProvider { public: - // The event that triggered the profile collection. - // This enum should be kept in sync with content/common/profiled_stack_state.h - enum Trigger { - UNKNOWN, - PROCESS_STARTUP, - JANKY_TASK, - THREAD_HUNG, - TRIGGER_LAST = THREAD_HUNG - }; - - // Parameters to pass back to the metrics provider. - struct Params { - explicit Params(Trigger trigger); - Params(Trigger trigger, bool preserve_sample_ordering); - - // The triggering event. - Trigger trigger; - - // True if sample ordering is important and should be preserved when the - // associated profiles are compressed. This should only be set to true if - // the intended use of the requires that the sequence of call stacks within - // a particular profile be preserved. The default value of false provides - // better compression of the encoded profile and is sufficient for the - // typical use case of recording profiles for stack frequency analysis in - // aggregate. - bool preserve_sample_ordering; - }; - CallStackProfileMetricsProvider(); ~CallStackProfileMetricsProvider() override; @@ -54,7 +27,15 @@ class CallStackProfileMetricsProvider : public MetricsProvider { // StackSamplingProfiler, and should not be reused between // StackSamplingProfilers. This function may be called on any thread. static base::StackSamplingProfiler::CompletedCallback GetProfilerCallback( - const Params& params); + const CallStackProfileParams& params); + + // Provides completed stack profiles to the metrics provider. Intended for use + // when receiving profiles over IPC. In-process StackSamplingProfiler users + // should use GetProfilerCallback() instead. + static void ReceiveCompletedProfiles( + const CallStackProfileParams& params, + base::TimeTicks start_timestamp, + const base::StackSamplingProfiler::CallStackProfiles& profiles); // MetricsProvider: void OnRecordingEnabled() override; diff --git a/chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc b/chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc index e361137e168..e5dfb9ad9f6 100644 --- a/chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc +++ b/chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc @@ -13,6 +13,7 @@ #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "build/build_config.h" +#include "components/metrics/call_stack_profile_params.h" #include "components/metrics/proto/chrome_user_metrics_extension.pb.h" #include "components/variations/entropy_provider.h" #include "testing/gtest/include/gtest/gtest.h" @@ -26,8 +27,6 @@ using Sample = StackSamplingProfiler::Sample; namespace metrics { -using Params = CallStackProfileMetricsProvider::Params; - // This test fixture enables the field trial that // CallStackProfileMetricsProvider depends on to report profiles. class CallStackProfileMetricsProviderTest : public testing::Test { @@ -43,7 +42,8 @@ class CallStackProfileMetricsProviderTest : public testing::Test { ~CallStackProfileMetricsProviderTest() override {} // Utility function to append profiles to the metrics provider. - void AppendProfiles(const Params& params, const Profiles& profiles) { + void AppendProfiles(const CallStackProfileParams& params, + const Profiles& profiles) { CallStackProfileMetricsProvider::GetProfilerCallback(params).Run(profiles); } @@ -205,7 +205,10 @@ TEST_F(CallStackProfileMetricsProviderTest, MultipleProfiles) { CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); AppendProfiles( - Params(CallStackProfileMetricsProvider::PROCESS_STARTUP, false), + CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS, + CallStackProfileParams::UI_THREAD, + CallStackProfileParams::PROCESS_STARTUP, + CallStackProfileParams::MAY_SHUFFLE), profiles); ChromeUserMetricsExtension uma_proto; provider.ProvideGeneralMetrics(&uma_proto); @@ -266,6 +269,10 @@ TEST_F(CallStackProfileMetricsProviderTest, MultipleProfiles) { 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()); } @@ -313,7 +320,10 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) { CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); AppendProfiles( - Params(CallStackProfileMetricsProvider::PROCESS_STARTUP, false), + CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS, + CallStackProfileParams::UI_THREAD, + CallStackProfileParams::PROCESS_STARTUP, + CallStackProfileParams::MAY_SHUFFLE), std::vector<Profile>(1, profile)); ChromeUserMetricsExtension uma_proto; provider.ProvideGeneralMetrics(&uma_proto); @@ -392,7 +402,10 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) { CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); - AppendProfiles(Params(CallStackProfileMetricsProvider::PROCESS_STARTUP, true), + AppendProfiles(CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS, + CallStackProfileParams::UI_THREAD, + CallStackProfileParams::PROCESS_STARTUP, + CallStackProfileParams::PRESERVE_ORDER), std::vector<Profile>(1, profile)); ChromeUserMetricsExtension uma_proto; provider.ProvideGeneralMetrics(&uma_proto); @@ -444,7 +457,10 @@ TEST_F(CallStackProfileMetricsProviderTest, UnknownModule) { CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); AppendProfiles( - Params(CallStackProfileMetricsProvider::PROCESS_STARTUP, false), + CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS, + CallStackProfileParams::UI_THREAD, + CallStackProfileParams::PROCESS_STARTUP, + CallStackProfileParams::MAY_SHUFFLE), std::vector<Profile>(1, profile)); ChromeUserMetricsExtension uma_proto; provider.ProvideGeneralMetrics(&uma_proto); @@ -481,7 +497,10 @@ TEST_F(CallStackProfileMetricsProviderTest, ProfilesProvidedOnlyOnce) { provider.OnRecordingEnabled(); AppendProfiles( - Params(CallStackProfileMetricsProvider::PROCESS_STARTUP, false), + CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS, + CallStackProfileParams::UI_THREAD, + CallStackProfileParams::PROCESS_STARTUP, + CallStackProfileParams::MAY_SHUFFLE), std::vector<Profile>(1, profile)); ChromeUserMetricsExtension uma_proto; provider.ProvideGeneralMetrics(&uma_proto); @@ -508,7 +527,10 @@ TEST_F(CallStackProfileMetricsProviderTest, profile.sampling_period = base::TimeDelta::FromMilliseconds(10); AppendProfiles( - Params(CallStackProfileMetricsProvider::PROCESS_STARTUP, false), + CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS, + CallStackProfileParams::UI_THREAD, + CallStackProfileParams::PROCESS_STARTUP, + CallStackProfileParams::MAY_SHUFFLE), std::vector<Profile>(1, profile)); CallStackProfileMetricsProvider provider; @@ -532,7 +554,10 @@ TEST_F(CallStackProfileMetricsProviderTest, ProfilesNotProvidedWhileDisabled) { CallStackProfileMetricsProvider provider; provider.OnRecordingDisabled(); AppendProfiles( - Params(CallStackProfileMetricsProvider::PROCESS_STARTUP, false), + CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS, + CallStackProfileParams::UI_THREAD, + CallStackProfileParams::PROCESS_STARTUP, + CallStackProfileParams::MAY_SHUFFLE), std::vector<Profile>(1, profile)); ChromeUserMetricsExtension uma_proto; provider.ProvideGeneralMetrics(&uma_proto); @@ -555,7 +580,10 @@ TEST_F(CallStackProfileMetricsProviderTest, provider.OnRecordingEnabled(); base::StackSamplingProfiler::CompletedCallback callback = CallStackProfileMetricsProvider::GetProfilerCallback( - Params(CallStackProfileMetricsProvider::PROCESS_STARTUP, false)); + CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS, + CallStackProfileParams::UI_THREAD, + CallStackProfileParams::PROCESS_STARTUP, + CallStackProfileParams::MAY_SHUFFLE)); provider.OnRecordingDisabled(); callback.Run(std::vector<Profile>(1, profile)); @@ -580,7 +608,10 @@ TEST_F(CallStackProfileMetricsProviderTest, provider.OnRecordingEnabled(); base::StackSamplingProfiler::CompletedCallback callback = CallStackProfileMetricsProvider::GetProfilerCallback( - Params(CallStackProfileMetricsProvider::PROCESS_STARTUP, false)); + CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS, + CallStackProfileParams::UI_THREAD, + CallStackProfileParams::PROCESS_STARTUP, + CallStackProfileParams::MAY_SHUFFLE)); provider.OnRecordingDisabled(); provider.OnRecordingEnabled(); @@ -606,7 +637,10 @@ TEST_F(CallStackProfileMetricsProviderTest, provider.OnRecordingDisabled(); base::StackSamplingProfiler::CompletedCallback callback = CallStackProfileMetricsProvider::GetProfilerCallback( - Params(CallStackProfileMetricsProvider::PROCESS_STARTUP, false)); + CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS, + CallStackProfileParams::UI_THREAD, + CallStackProfileParams::PROCESS_STARTUP, + CallStackProfileParams::MAY_SHUFFLE)); provider.OnRecordingEnabled(); callback.Run(std::vector<Profile>(1, profile)); diff --git a/chromium/components/metrics/call_stack_profile_params.cc b/chromium/components/metrics/call_stack_profile_params.cc new file mode 100644 index 00000000000..e937283616c --- /dev/null +++ b/chromium/components/metrics/call_stack_profile_params.cc @@ -0,0 +1,22 @@ +// 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_params.h" + +namespace metrics { + +CallStackProfileParams::CallStackProfileParams() + : CallStackProfileParams(UNKNOWN_PROCESS, UNKNOWN_THREAD, UNKNOWN) {} + +CallStackProfileParams::CallStackProfileParams(Process process, Thread thread, + Trigger trigger) + : CallStackProfileParams(process, thread, trigger, MAY_SHUFFLE) {} + +CallStackProfileParams::CallStackProfileParams(Process process, Thread thread, + Trigger trigger, + SampleOrderingSpec ordering_spec) + : process(process), thread(thread), trigger(trigger), + ordering_spec(ordering_spec) {} + +} // namespace metrics diff --git a/chromium/components/metrics/call_stack_profile_params.h b/chromium/components/metrics/call_stack_profile_params.h new file mode 100644 index 00000000000..4dd7cd4fef2 --- /dev/null +++ b/chromium/components/metrics/call_stack_profile_params.h @@ -0,0 +1,87 @@ +// Copyright 2016 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. + +#ifndef COMPONENTS_METRICS_CALL_STACK_PROFILE_PARAMS_H_ +#define COMPONENTS_METRICS_CALL_STACK_PROFILE_PARAMS_H_ + +namespace metrics { + +// Parameters to pass back to the metrics provider. +struct CallStackProfileParams { + // The process in which the collection occurred. + enum Process { + UNKNOWN_PROCESS, + BROWSER_PROCESS, + RENDERER_PROCESS, + GPU_PROCESS, + UTILITY_PROCESS, + ZYGOTE_PROCESS, + SANDBOX_HELPER_PROCESS, + PPAPI_PLUGIN_PROCESS, + PPAPI_BROKER_PROCESS + }; + + // The thread from which the collection occurred. + enum Thread { + UNKNOWN_THREAD, + + // Browser process threads, some of which occur in other processes as well. + UI_THREAD, + FILE_THREAD, + FILE_USER_BLOCKING_THREAD, + PROCESS_LAUNCHER_THREAD, + CACHE_THREAD, + IO_THREAD, + DB_THREAD, + + // GPU process thread. + GPU_MAIN_THREAD, + + // Renderer process threads. + RENDER_THREAD, + UTILITY_THREAD + }; + + // The event that triggered the profile collection. + enum Trigger { + UNKNOWN, + PROCESS_STARTUP, + JANKY_TASK, + THREAD_HUNG, + TRIGGER_LAST = THREAD_HUNG + }; + + // Allows the caller to specify whether sample ordering is + // important. MAY_SHUFFLE should always be used to enable better compression, + // unless the use case needs order to be preserved for a specific reason. + enum SampleOrderingSpec { + // The provider may shuffle the sample order to improve compression. + MAY_SHUFFLE, + // The provider will not change the sample order. + PRESERVE_ORDER + }; + + // The default constructor is required for mojo and should not be used + // otherwise. A valid trigger should always be specified. + CallStackProfileParams(); + CallStackProfileParams(Process process, Thread thread, Trigger trigger); + CallStackProfileParams(Process process, Thread thread, Trigger trigger, + SampleOrderingSpec ordering_spec); + + // The collection process. + Process process; + + // The collection thread. + Thread thread; + + // The triggering event. + Trigger trigger; + + // Whether to preserve sample ordering. + SampleOrderingSpec ordering_spec; +}; + +} // namespace metrics + +#endif // COMPONENTS_METRICS_CALL_STACK_PROFILE_PARAMS_H_ diff --git a/chromium/components/metrics/child_call_stack_profile_collector.cc b/chromium/components/metrics/child_call_stack_profile_collector.cc new file mode 100644 index 00000000000..9c62af85568 --- /dev/null +++ b/chromium/components/metrics/child_call_stack_profile_collector.cc @@ -0,0 +1,86 @@ +// Copyright 2016 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/child_call_stack_profile_collector.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/synchronization/lock.h" +#include "base/threading/thread_task_runner_handle.h" +#include "services/shell/public/cpp/interface_provider.h" + +namespace metrics { + +ChildCallStackProfileCollector::ProfilesState::ProfilesState() = default; +ChildCallStackProfileCollector::ProfilesState::ProfilesState( + const ProfilesState&) = default; + +ChildCallStackProfileCollector::ProfilesState::ProfilesState( + const CallStackProfileParams& params, + base::TimeTicks start_timestamp, + const base::StackSamplingProfiler::CallStackProfiles& profiles) + : params(params), start_timestamp(start_timestamp), profiles(profiles) {} + +ChildCallStackProfileCollector::ProfilesState::~ProfilesState() = default; + +ChildCallStackProfileCollector::ChildCallStackProfileCollector() {} + +ChildCallStackProfileCollector::~ChildCallStackProfileCollector() {} + +base::StackSamplingProfiler::CompletedCallback +ChildCallStackProfileCollector::GetProfilerCallback( + const CallStackProfileParams& params) { + return base::Bind(&ChildCallStackProfileCollector::Collect, + // This class has lazy instance lifetime. + base::Unretained(this), params, + base::TimeTicks::Now()); +} + +void ChildCallStackProfileCollector::SetParentProfileCollector( + metrics::mojom::CallStackProfileCollectorPtr parent_collector) { + base::AutoLock alock(lock_); + // This function should only invoked once, during the mode of operation when + // retaining profiles after construction. + DCHECK(retain_profiles_); + retain_profiles_ = false; + task_runner_ = base::ThreadTaskRunnerHandle::Get(); + parent_collector_ = std::move(parent_collector); + if (parent_collector_) { + for (const ProfilesState& state : profiles_) { + parent_collector_->Collect(state.params, state.start_timestamp, + state.profiles); + } + } + profiles_.clear(); +} + +void ChildCallStackProfileCollector::Collect( + const CallStackProfileParams& params, + base::TimeTicks start_timestamp, + const std::vector<CallStackProfile>& profiles) { + base::AutoLock alock(lock_); + if (task_runner_ && + // The profiler thread does not have a task runner. Attempting to + // invoke Get() on it results in a DCHECK. + (!base::ThreadTaskRunnerHandle::IsSet() || + base::ThreadTaskRunnerHandle::Get() != task_runner_)) { + // Post back to the thread that owns the the parent interface. + task_runner_->PostTask(FROM_HERE, base::Bind( + &ChildCallStackProfileCollector::Collect, + // This class has lazy instance lifetime. + base::Unretained(this), + params, + start_timestamp, + profiles)); + return; + } + + if (parent_collector_) { + parent_collector_->Collect(params, start_timestamp, profiles); + } else if (retain_profiles_) { + profiles_.push_back(ProfilesState(params, start_timestamp, profiles)); + } +} + +} // namespace metrics diff --git a/chromium/components/metrics/child_call_stack_profile_collector.h b/chromium/components/metrics/child_call_stack_profile_collector.h new file mode 100644 index 00000000000..adc356d110f --- /dev/null +++ b/chromium/components/metrics/child_call_stack_profile_collector.h @@ -0,0 +1,120 @@ +// Copyright 2016 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. + +#ifndef COMPONENTS_METRICS_CHILD_CALL_STACK_PROFILE_COLLECTOR_H_ +#define COMPONENTS_METRICS_CHILD_CALL_STACK_PROFILE_COLLECTOR_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/single_thread_task_runner.h" +#include "base/synchronization/lock.h" +#include "components/metrics/public/interfaces/call_stack_profile_collector.mojom.h" + +namespace shell { +class InterfaceProvider; +} + +namespace metrics { + +// ChildCallStackProfileCollector collects stacks at startup, caching them +// internally until a CallStackProfileCollector interface is available. If a +// CallStackProfileCollector is provided via the InterfaceProvider supplied to +// SetParentProfileCollector, the cached stacks are sent via that interface. All +// future stacks received via callbacks supplied by GetProfilerCallback are sent +// via that interface as well. +// +// If no CallStackProfileCollector is provided via InterfaceProvider, any cached +// stacks and all future stacks received via callbacks supplied by +// GetProfilerCallback are flushed. In typical usage this should not happen +// because the browser is expected to always supply a CallStackProfileCollector. +// +// This class is only necessary if a CallStackProfileCollector is not available +// at the time the profiler is created. Otherwise the CallStackProfileCollector +// can be used directly. +// +// To use, create as a leaky lazy instance: +// +// base::LazyInstance<metrics::ChildCallStackProfileCollector>::Leaky +// g_call_stack_profile_collector = LAZY_INSTANCE_INITIALIZER; +// +// Then, invoke CreateCompletedCallback() to generate the CompletedCallback to +// pass when creating the StackSamplingProfiler. +// +// When the mojo InterfaceProvider becomes available, provide it via +// SetParentProfileCollector(). +class ChildCallStackProfileCollector { + public: + ChildCallStackProfileCollector(); + ~ChildCallStackProfileCollector(); + + // Get a callback for use with StackSamplingProfiler that provides completed + // profiles to this object. The callback should be immediately passed to the + // StackSamplingProfiler, and should not be reused between + // StackSamplingProfilers. This function may be called on any thread. + base::StackSamplingProfiler::CompletedCallback GetProfilerCallback( + const CallStackProfileParams& params); + + // Sets the CallStackProfileCollector interface from |parent_collector|. This + // function MUST be invoked exactly once, regardless of whether + // |parent_collector| is null, as it flushes pending data in either case. + void SetParentProfileCollector( + metrics::mojom::CallStackProfileCollectorPtr parent_collector); + + private: + friend class ChildCallStackProfileCollectorTest; + + // Bundles together a set of collected profiles and the collection state for + // storage, pending availability of the parent mojo interface. + struct ProfilesState { + ProfilesState(); + ProfilesState(const ProfilesState&); + ProfilesState( + const CallStackProfileParams& params, + base::TimeTicks start_timestamp, + const base::StackSamplingProfiler::CallStackProfiles& profiles); + ~ProfilesState(); + + CallStackProfileParams params; + base::TimeTicks start_timestamp; + + // The sampled profiles. + base::StackSamplingProfiler::CallStackProfiles profiles; + }; + + using CallStackProfile = base::StackSamplingProfiler::CallStackProfile; + + void Collect(const CallStackProfileParams& params, + base::TimeTicks start_timestamp, + const std::vector<CallStackProfile>& profiles); + + // This object may be accessed on any thread, including the profiler + // thread. The expected use case for the object is to be created and have + // GetProfilerCallback before the message loop starts, which prevents the use + // of PostTask and the like for inter-thread communication. + base::Lock lock_; + + // Whether to retain profiles when the interface is not set. Remains true + // until the invocation of SetParentProfileCollector(), at which point it is + // false for the rest of the object lifetime. + bool retain_profiles_ = true; + + // The task runner associated with the parent interface. + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + + // The interface to use to collect the stack profiles provided to this + // object. Initially null until SetParentProfileCollector() is invoked, at + // which point it may either become set or remain null. If set, stacks are + // collected via the interface, otherwise they are ignored. + mojom::CallStackProfileCollectorPtr parent_collector_; + + // Profiles being cached by this object, pending a parent interface to be + // supplied. + std::vector<ProfilesState> profiles_; + + DISALLOW_COPY_AND_ASSIGN(ChildCallStackProfileCollector); +}; + +} // namespace metrics + +#endif // COMPONENTS_METRICS_CHILD_CALL_STACK_PROFILE_COLLECTOR_H_ diff --git a/chromium/components/metrics/child_call_stack_profile_collector_unittest.cc b/chromium/components/metrics/child_call_stack_profile_collector_unittest.cc new file mode 100644 index 00000000000..556124feed9 --- /dev/null +++ b/chromium/components/metrics/child_call_stack_profile_collector_unittest.cc @@ -0,0 +1,175 @@ +// Copyright 2016 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/child_call_stack_profile_collector.h" + +#include <memory> +#include <vector> + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "components/metrics/call_stack_profile_params.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "services/shell/public/cpp/interface_provider.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace metrics { + +namespace { + + +} // namespace + +class ChildCallStackProfileCollectorTest : public testing::Test { + protected: + class Receiver : public mojom::CallStackProfileCollector { + public: + using CallStackProfile = base::StackSamplingProfiler::CallStackProfile; + + Receiver(mojom::CallStackProfileCollectorRequest request) + : binding_(this, std::move(request)) {} + ~Receiver() override {} + + void Collect(const CallStackProfileParams& params, + base::TimeTicks start_timestamp, + const std::vector<CallStackProfile>& profiles) override { + this->profiles.push_back(ChildCallStackProfileCollector::ProfilesState( + params, + start_timestamp, + profiles)); + } + + std::vector<ChildCallStackProfileCollector::ProfilesState> profiles; + + private: + mojo::Binding<mojom::CallStackProfileCollector> binding_; + + DISALLOW_COPY_AND_ASSIGN(Receiver); + }; + + ChildCallStackProfileCollectorTest() + : receiver_impl_(new Receiver(GetProxy(&receiver_))) {} + + void CollectProfiles( + const CallStackProfileParams& params, + const base::StackSamplingProfiler::CallStackProfiles& profiles) { + child_collector_.GetProfilerCallback(params).Run(profiles); + } + + const std::vector<ChildCallStackProfileCollector::ProfilesState>& + profiles() const { + return child_collector_.profiles_; + } + + base::MessageLoop loop_; + mojom::CallStackProfileCollectorPtr receiver_; + std::unique_ptr<Receiver> receiver_impl_; + ChildCallStackProfileCollector child_collector_; + + DISALLOW_COPY_AND_ASSIGN(ChildCallStackProfileCollectorTest); +}; + +// Test the behavior when an interface is provided. +TEST_F(ChildCallStackProfileCollectorTest, InterfaceProvided) { + EXPECT_EQ(0u, profiles().size()); + + // Add profiles before providing the interface. + CollectProfiles( + CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS, + CallStackProfileParams::UI_THREAD, + CallStackProfileParams::JANKY_TASK, + CallStackProfileParams::PRESERVE_ORDER), + { base::StackSamplingProfiler::CallStackProfile(), + base::StackSamplingProfiler::CallStackProfile() }); + ASSERT_EQ(1u, profiles().size()); + EXPECT_EQ(CallStackProfileParams::BROWSER_PROCESS, + profiles()[0].params.process); + EXPECT_EQ(CallStackProfileParams::UI_THREAD, profiles()[0].params.thread); + EXPECT_EQ(CallStackProfileParams::JANKY_TASK, profiles()[0].params.trigger); + EXPECT_EQ(CallStackProfileParams::PRESERVE_ORDER, + profiles()[0].params.ordering_spec); + base::TimeTicks start_timestamp = profiles()[0].start_timestamp; + EXPECT_GE(base::TimeDelta::FromMilliseconds(10), + base::TimeTicks::Now() - start_timestamp); + EXPECT_EQ(2u, profiles()[0].profiles.size()); + + // Set the interface. The profiles should be passed to it. + child_collector_.SetParentProfileCollector(std::move(receiver_)); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0u, profiles().size()); + ASSERT_EQ(1u, receiver_impl_->profiles.size()); + EXPECT_EQ(CallStackProfileParams::JANKY_TASK, + receiver_impl_->profiles[0].params.trigger); + EXPECT_EQ(CallStackProfileParams::PRESERVE_ORDER, + receiver_impl_->profiles[0].params.ordering_spec); + EXPECT_EQ(start_timestamp, receiver_impl_->profiles[0].start_timestamp); + EXPECT_EQ(2u, receiver_impl_->profiles[0].profiles.size()); + + // Add profiles after providing the interface. They should also be passed to + // it. + receiver_impl_->profiles.clear(); + CollectProfiles( + CallStackProfileParams(CallStackProfileParams::GPU_PROCESS, + CallStackProfileParams::GPU_MAIN_THREAD, + CallStackProfileParams::THREAD_HUNG, + CallStackProfileParams::PRESERVE_ORDER), + { base::StackSamplingProfiler::CallStackProfile() }); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0u, profiles().size()); + ASSERT_EQ(1u, receiver_impl_->profiles.size()); + EXPECT_EQ(CallStackProfileParams::GPU_PROCESS, + receiver_impl_->profiles[0].params.process); + EXPECT_EQ(CallStackProfileParams::GPU_MAIN_THREAD, + receiver_impl_->profiles[0].params.thread); + EXPECT_EQ(CallStackProfileParams::THREAD_HUNG, + receiver_impl_->profiles[0].params.trigger); + EXPECT_EQ(CallStackProfileParams::PRESERVE_ORDER, + receiver_impl_->profiles[0].params.ordering_spec); + EXPECT_GE(base::TimeDelta::FromMilliseconds(10), + (base::TimeTicks::Now() - + receiver_impl_->profiles[0].start_timestamp)); + EXPECT_EQ(1u, receiver_impl_->profiles[0].profiles.size()); +} + +TEST_F(ChildCallStackProfileCollectorTest, InterfaceNotProvided) { + EXPECT_EQ(0u, profiles().size()); + + // Add profiles before providing a null interface. + CollectProfiles( + CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS, + CallStackProfileParams::UI_THREAD, + CallStackProfileParams::JANKY_TASK, + CallStackProfileParams::PRESERVE_ORDER), + { base::StackSamplingProfiler::CallStackProfile(), + base::StackSamplingProfiler::CallStackProfile() }); + ASSERT_EQ(1u, profiles().size()); + EXPECT_EQ(CallStackProfileParams::BROWSER_PROCESS, + profiles()[0].params.process); + EXPECT_EQ(CallStackProfileParams::UI_THREAD, profiles()[0].params.thread); + EXPECT_EQ(CallStackProfileParams::JANKY_TASK, profiles()[0].params.trigger); + EXPECT_EQ(CallStackProfileParams::PRESERVE_ORDER, + profiles()[0].params.ordering_spec); + EXPECT_GE(base::TimeDelta::FromMilliseconds(10), + base::TimeTicks::Now() - profiles()[0].start_timestamp); + EXPECT_EQ(2u, profiles()[0].profiles.size()); + + // Set the null interface. The profiles should be flushed. + child_collector_.SetParentProfileCollector( + mojom::CallStackProfileCollectorPtr()); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0u, profiles().size()); + + // Add profiles after providing a null interface. They should also be flushed. + CollectProfiles( + CallStackProfileParams(CallStackProfileParams::GPU_PROCESS, + CallStackProfileParams::GPU_MAIN_THREAD, + CallStackProfileParams::THREAD_HUNG, + CallStackProfileParams::PRESERVE_ORDER), + { base::StackSamplingProfiler::CallStackProfile() }); + EXPECT_EQ(0u, profiles().size()); +} + +} // namespace metrics diff --git a/chromium/components/metrics/file_metrics_provider.cc b/chromium/components/metrics/file_metrics_provider.cc index 2d331d63bfa..aa81a0cd1f0 100644 --- a/chromium/components/metrics/file_metrics_provider.cc +++ b/chromium/components/metrics/file_metrics_provider.cc @@ -325,8 +325,8 @@ FileMetricsProvider::AccessResult FileMetricsProvider::CheckAndMapMetricSource( // Create an allocator for the mapped file. Ownership passes to the allocator. source->allocator.reset(new base::PersistentHistogramAllocator( - base::WrapUnique(new base::FilePersistentMemoryAllocator( - std::move(mapped), 0, 0, base::StringPiece(), read_only)))); + base::MakeUnique<base::FilePersistentMemoryAllocator>( + std::move(mapped), 0, 0, base::StringPiece(), read_only))); return ACCESS_RESULT_SUCCESS; } @@ -372,7 +372,7 @@ void FileMetricsProvider::RecordHistogramSnapshotsFromSource( std::unique_ptr<base::HistogramBase> histogram = histogram_iter.GetNext(); if (!histogram) break; - snapshot_manager->PrepareFinalDeltaTakingOwnership(std::move(histogram)); + snapshot_manager->PrepareFinalDelta(histogram.get()); ++histogram_count; } diff --git a/chromium/components/metrics/file_metrics_provider_unittest.cc b/chromium/components/metrics/file_metrics_provider_unittest.cc index d1d340b9f95..11977099c3e 100644 --- a/chromium/components/metrics/file_metrics_provider_unittest.cc +++ b/chromium/components/metrics/file_metrics_provider_unittest.cc @@ -95,9 +95,9 @@ class FileMetricsProviderTest : public testing::TestWithParam<bool> { } TestingPrefServiceSimple* prefs() { return prefs_.get(); } - base::FilePath temp_dir() { return temp_dir_.path(); } + base::FilePath temp_dir() { return temp_dir_.GetPath(); } base::FilePath metrics_file() { - return temp_dir_.path().AppendASCII(kMetricsFilename); + return temp_dir_.GetPath().AppendASCII(kMetricsFilename); } FileMetricsProvider* provider() { @@ -286,36 +286,40 @@ TEST_P(FileMetricsProviderTest, AccessDirectory) { // ensure that each file has a later timestamp on disk than the previous one. base::ScopedTempDir metrics_files; EXPECT_TRUE(metrics_files.CreateUniqueTempDir()); - WriteMetricsFileAtTime(metrics_files.path().AppendASCII(".foo.pma"), + WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII(".foo.pma"), allocator, base_time); - WriteMetricsFileAtTime(metrics_files.path().AppendASCII("_bar.pma"), + WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("_bar.pma"), allocator, base_time); histogram = base::Histogram::FactoryGet("h1", 1, 100, 10, 0); histogram->Add(1); - WriteMetricsFileAtTime(metrics_files.path().AppendASCII("a1.pma"), allocator, + WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("a1.pma"), + allocator, base_time + base::TimeDelta::FromMinutes(1)); histogram = base::Histogram::FactoryGet("h2", 1, 100, 10, 0); histogram->Add(2); - WriteMetricsFileAtTime(metrics_files.path().AppendASCII("c2.pma"), allocator, + WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("c2.pma"), + allocator, base_time + base::TimeDelta::FromMinutes(2)); histogram = base::Histogram::FactoryGet("h3", 1, 100, 10, 0); histogram->Add(3); - WriteMetricsFileAtTime(metrics_files.path().AppendASCII("b3.pma"), allocator, + WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("b3.pma"), + allocator, base_time + base::TimeDelta::FromMinutes(3)); histogram = base::Histogram::FactoryGet("h4", 1, 100, 10, 0); histogram->Add(3); - WriteMetricsFileAtTime(metrics_files.path().AppendASCII("d4.pma"), allocator, + WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("d4.pma"), + allocator, base_time + base::TimeDelta::FromMinutes(4)); - base::TouchFile(metrics_files.path().AppendASCII("b3.pma"), + base::TouchFile(metrics_files.GetPath().AppendASCII("b3.pma"), base_time + base::TimeDelta::FromMinutes(5), base_time + base::TimeDelta::FromMinutes(5)); - WriteMetricsFileAtTime(metrics_files.path().AppendASCII("baz"), allocator, + WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("baz"), allocator, base_time + base::TimeDelta::FromMinutes(6)); // The global allocator has to be detached here so that no metrics created @@ -324,7 +328,7 @@ TEST_P(FileMetricsProviderTest, AccessDirectory) { base::GlobalHistogramAllocator::ReleaseForTesting(); // Register the file and allow the "checker" task to run. - provider()->RegisterSource(metrics_files.path(), + provider()->RegisterSource(metrics_files.GetPath(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_DIR, FileMetricsProvider::ASSOCIATE_CURRENT_RUN, kMetricsName); @@ -339,13 +343,15 @@ TEST_P(FileMetricsProviderTest, AccessDirectory) { EXPECT_EQ(expect_order[i], GetSnapshotHistogramCount()) << i; } - EXPECT_FALSE(base::PathExists(metrics_files.path().AppendASCII("a1.pma"))); - EXPECT_FALSE(base::PathExists(metrics_files.path().AppendASCII("c2.pma"))); - EXPECT_FALSE(base::PathExists(metrics_files.path().AppendASCII("b3.pma"))); - EXPECT_FALSE(base::PathExists(metrics_files.path().AppendASCII("d4.pma"))); - EXPECT_TRUE(base::PathExists(metrics_files.path().AppendASCII(".foo.pma"))); - EXPECT_TRUE(base::PathExists(metrics_files.path().AppendASCII("_bar.pma"))); - EXPECT_TRUE(base::PathExists(metrics_files.path().AppendASCII("baz"))); + EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("a1.pma"))); + EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("c2.pma"))); + EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("b3.pma"))); + EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("d4.pma"))); + EXPECT_TRUE( + base::PathExists(metrics_files.GetPath().AppendASCII(".foo.pma"))); + EXPECT_TRUE( + base::PathExists(metrics_files.GetPath().AppendASCII("_bar.pma"))); + EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("baz"))); } TEST_P(FileMetricsProviderTest, AccessReadWriteMetrics) { @@ -411,9 +417,7 @@ TEST_P(FileMetricsProviderTest, AccessInitialMetrics) { { HistogramFlattenerDeltaRecorder flattener; base::HistogramSnapshotManager snapshot_manager(&flattener); - snapshot_manager.StartDeltas(); RecordInitialHistogramSnapshots(&snapshot_manager); - snapshot_manager.FinishDeltas(); EXPECT_EQ(2U, flattener.GetRecordedDeltaHistogramNames().size()); } EXPECT_TRUE(base::PathExists(metrics_file())); diff --git a/chromium/components/metrics/leak_detector/BUILD.gn b/chromium/components/metrics/leak_detector/BUILD.gn new file mode 100644 index 00000000000..0e13520174f --- /dev/null +++ b/chromium/components/metrics/leak_detector/BUILD.gn @@ -0,0 +1,66 @@ +# Copyright 2016 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. + +import("//mojo/public/tools/bindings/mojom.gni") + +if (is_chromeos) { + source_set("leak_detector") { + sources = [ + "call_stack_manager.cc", + "call_stack_manager.h", + "call_stack_table.cc", + "call_stack_table.h", + "custom_allocator.cc", + "custom_allocator.h", + "gnu_build_id_reader.cc", + "gnu_build_id_reader.h", + "leak_analyzer.cc", + "leak_analyzer.h", + "leak_detector.cc", + "leak_detector.h", + "leak_detector_impl.cc", + "leak_detector_impl.h", + "leak_detector_value_type.cc", + "leak_detector_value_type.h", + "protobuf_to_mojo_converter.cc", + "protobuf_to_mojo_converter.h", + "ranked_set.cc", + "ranked_set.h", + ] + + deps = [ + ":interfaces", + "//base", + "//components/metrics/proto:proto", + ] + } + + mojom("interfaces") { + sources = [ + "leak_detector.mojom", + ] + } + + source_set("unit_tests") { + testonly = true + sources = [ + "call_stack_manager_unittest.cc", + "call_stack_table_unittest.cc", + "leak_analyzer_unittest.cc", + "leak_detector_impl_unittest.cc", + "leak_detector_unittest.cc", + "protobuf_to_mojo_converter_unittest.cc", + "ranked_set_unittest.cc", + ] + + deps = [ + ":interfaces", + ":leak_detector", + "//base", + "//components/metrics/proto:proto", + "//content/test:test_support", + "//testing/gtest", + ] + } +} diff --git a/chromium/components/metrics/leak_detector/OWNERS b/chromium/components/metrics/leak_detector/OWNERS index d8bfc45aa54..cde5a71325c 100644 --- a/chromium/components/metrics/leak_detector/OWNERS +++ b/chromium/components/metrics/leak_detector/OWNERS @@ -1,2 +1,7 @@ sque@chromium.org wfh@chromium.org + +# Changes to Mojo interfaces require a security review to avoid +# introducing new sandbox escapes. +per-file *.mojom=set noparent +per-file *.mojom=file://ipc/SECURITY_OWNERS diff --git a/chromium/components/metrics/leak_detector/gnu_build_id_reader.cc b/chromium/components/metrics/leak_detector/gnu_build_id_reader.cc new file mode 100644 index 00000000000..ac0f07daecc --- /dev/null +++ b/chromium/components/metrics/leak_detector/gnu_build_id_reader.cc @@ -0,0 +1,124 @@ +// Copyright 2016 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/leak_detector/gnu_build_id_reader.h" + +#include <elf.h> + +#include <algorithm> + +#if defined(OS_CHROMEOS) +#include <link.h> // for dl_iterate_phdr +#else +#error "Getting binary mapping info is not supported on this platform." +#endif // defined(OS_CHROMEOS) + +namespace metrics { +namespace leak_detector { +namespace gnu_build_id_reader { + +namespace { + +// Contains data passed to dl_iterate_phdr() for reading build ID. +struct ReadBuildIDData { + // Points to a vector for storing the build ID. + std::vector<uint8_t>* build_id; + // Indicates whether build ID was read successfully. + bool success; +}; + +// Given a pointer and an offset, add the offset to the pointer and round it up +// to the next uint32_t. +const void* AlignPtrAndOffsetToUint32(const void* ptr, intptr_t offset) { + uintptr_t addr = reinterpret_cast<uintptr_t>(ptr) + offset; + uintptr_t aligned_addr = + (addr + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1); + return reinterpret_cast<const void*>(aligned_addr); +} + +// Searches for the ELF note containing the build ID within the data range +// specified by [start, end). Returns the build ID in |*result|. If the build ID +// is not found, |*result| will be unchanged. Returns true if the build ID was +// successfully read or false otherwise. +bool GetBuildIdFromNotes(const void* start, + const void* end, + std::vector<uint8_t>* result) { + using NoteHeaderPtr = const ElfW(Nhdr)*; + NoteHeaderPtr note = reinterpret_cast<NoteHeaderPtr>(start); + + while (note < end) { + const char* name_ptr = reinterpret_cast<const char*>(note + 1); + if (name_ptr >= end) { + break; + } + // |desc_ptr| points the to the actual build ID data. + const uint8_t* desc_ptr = reinterpret_cast<const uint8_t*>( + AlignPtrAndOffsetToUint32(name_ptr, note->n_namesz)); + if (note->n_type == NT_GNU_BUILD_ID && + note->n_namesz == sizeof(ELF_NOTE_GNU) && + std::equal(name_ptr, name_ptr + sizeof(ELF_NOTE_GNU), ELF_NOTE_GNU)) { + result->assign(desc_ptr, desc_ptr + note->n_descsz); + return true; + } + NoteHeaderPtr next_ptr = reinterpret_cast<NoteHeaderPtr>( + AlignPtrAndOffsetToUint32(desc_ptr, note->n_descsz)); + note = next_ptr; + } + return false; +} + +// Callback for dl_iterate_phdr(). Finds the notes section and looks for the +// build ID in there. |data| points to a ReadBuildIDData struct whose |build_id| +// field should point to a valid std::vector<uint8_t>. Returns the build ID in +// that field, and sets |ReadBuildIDData::success| based on whether the build ID +// was successfully read. +// +// This function always returns 1 to signal the end of dl_iterate_phdr()'s +// iteration, because it is only interested in the first binary iterated by +// dl_iterate_phdr(), which is the current binary. +int FindNotesAndGetBuildID(struct dl_phdr_info* info, + size_t /* size */, + void* data) { + uintptr_t mapping_addr = reinterpret_cast<uintptr_t>(info->dlpi_addr); + const ElfW(Ehdr)* file_header = + reinterpret_cast<const ElfW(Ehdr)*>(mapping_addr); + + // Make sure that a valid |mapping_addr| was read. + if (!file_header || file_header->e_phentsize != sizeof(ElfW(Phdr))) { + return 1; + } + + // Find the ELF segment header for the NOTES section. + for (int i = 0; i < info->dlpi_phnum; ++i) { + const ElfW(Phdr)& segment_header = info->dlpi_phdr[i]; + if (segment_header.p_type == PT_NOTE) { + const void* note = reinterpret_cast<const void*>( + mapping_addr + segment_header.p_vaddr); + const void* note_end = reinterpret_cast<const void*>( + mapping_addr + segment_header.p_vaddr + segment_header.p_memsz); + ReadBuildIDData* read_data = reinterpret_cast<ReadBuildIDData*>(data); + read_data->success = + GetBuildIdFromNotes(note, note_end, read_data->build_id); + } + } + return 1; +} + +} // namespace + +bool ReadBuildID(std::vector<uint8_t>* build_id) { + ReadBuildIDData data; + data.build_id = build_id; + data.success = false; + +#if defined(OS_CHROMEOS) + dl_iterate_phdr(FindNotesAndGetBuildID, &data); +#endif // defined(OS_CHROMEOS) + + return data.success; +} + +} // namespace gnu_build_id_reader +} // namespace leak_detector +} // namespace metrics diff --git a/chromium/components/metrics/leak_detector/gnu_build_id_reader.h b/chromium/components/metrics/leak_detector/gnu_build_id_reader.h new file mode 100644 index 00000000000..5cda9f3869a --- /dev/null +++ b/chromium/components/metrics/leak_detector/gnu_build_id_reader.h @@ -0,0 +1,24 @@ +// Copyright 2016 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. + +#ifndef COMPONENTS_METRICS_LEAK_DETECTOR_GNU_BUILD_ID_READER_H_ +#define COMPONENTS_METRICS_LEAK_DETECTOR_GNU_BUILD_ID_READER_H_ + +#include <stdint.h> + +#include <vector> + +namespace metrics { +namespace leak_detector { +namespace gnu_build_id_reader { + +// Reads the build ID from the GNU build notes and stores it in |*build_id|. +// Returns true if it was successfully read, or false otherwise. +bool ReadBuildID(std::vector<uint8_t>* build_id); + +} // namespace gnu_build_id_reader +} // namespace leak_detector +} // namespace metrics + +#endif // COMPONENTS_METRICS_LEAK_DETECTOR_GNU_BUILD_ID_READER_H_ diff --git a/chromium/components/metrics/leak_detector/leak_detector.mojom b/chromium/components/metrics/leak_detector/leak_detector.mojom new file mode 100644 index 00000000000..d985b79024c --- /dev/null +++ b/chromium/components/metrics/leak_detector/leak_detector.mojom @@ -0,0 +1,40 @@ +// Copyright 2016 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. + +module metrics.mojom; + +struct LeakDetectorParams { + float sampling_rate; + uint32 max_stack_depth; + uint64 analysis_interval_bytes; + uint32 size_suspicion_threshold; + uint32 call_stack_suspicion_threshold; +}; + +struct AllocationBreakdown { + array<uint32> counts_by_size; + uint32 count_for_call_stack; +}; + +struct MemoryLeakReport { + uint32 size_bytes; + array<uint64> call_stack; + + array<AllocationBreakdown> alloc_breakdown_history; +}; + +// Provides a remote interface for enabling LeakDetector on remote processes. +interface LeakDetector { + // Returns a LeakDetectorParams struct. Used to indicate to the remote process + // what parameters to use when initializing LeakDetector. Can also return + // |params.sampling_rate| == 0 to indicate that LeakDetector should not be + // initialized on a particular process. + GetParams() => (LeakDetectorParams params); + + // When a remote process running LeakDetector gets some leak reports, it + // should call this function to return the leak reports back to the main + // process that implements this function. The reports should be sent back as + // an array of serialized MemoryLeakReportProtos. + SendLeakReports(array<MemoryLeakReport> reports); +}; diff --git a/chromium/components/metrics/leak_detector/protobuf_to_mojo_converter.cc b/chromium/components/metrics/leak_detector/protobuf_to_mojo_converter.cc new file mode 100644 index 00000000000..24202dbcb8c --- /dev/null +++ b/chromium/components/metrics/leak_detector/protobuf_to_mojo_converter.cc @@ -0,0 +1,67 @@ +// Copyright 2016 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/leak_detector/protobuf_to_mojo_converter.h" + +namespace metrics { +namespace leak_detector { +namespace protobuf_to_mojo_converter { + +void ParamsToMojo(const MemoryLeakReportProto::Params& params, + mojom::LeakDetectorParams* mojo_params) { + mojo_params->sampling_rate = params.sampling_rate(); + mojo_params->max_stack_depth = params.max_stack_depth(); + mojo_params->analysis_interval_bytes = params.analysis_interval_bytes(); + mojo_params->size_suspicion_threshold = params.size_suspicion_threshold(); + mojo_params->call_stack_suspicion_threshold = + params.call_stack_suspicion_threshold(); +} + +void MojoToParams(const mojom::LeakDetectorParams& mojo_params, + MemoryLeakReportProto::Params* params) { + params->set_sampling_rate(mojo_params.sampling_rate); + params->set_max_stack_depth(mojo_params.max_stack_depth); + params->set_analysis_interval_bytes(mojo_params.analysis_interval_bytes); + params->set_size_suspicion_threshold(mojo_params.size_suspicion_threshold); + params->set_call_stack_suspicion_threshold( + mojo_params.call_stack_suspicion_threshold); +} + +void ReportToMojo(const MemoryLeakReportProto& report, + mojom::MemoryLeakReport* mojo_report) { + mojo_report->size_bytes = report.size_bytes(); + for (auto call_stack_value : report.call_stack()) { + mojo_report->call_stack.push_back(call_stack_value); + } + + for (const auto& history_entry : report.alloc_breakdown_history()) { + metrics::mojom::AllocationBreakdownPtr mojo_entry = + metrics::mojom::AllocationBreakdown::New(); + for (auto count : history_entry.counts_by_size()) { + mojo_entry->counts_by_size.push_back(count); + } + mojo_entry->count_for_call_stack = history_entry.count_for_call_stack(); + + mojo_report->alloc_breakdown_history.push_back(std::move(mojo_entry)); + } +} + +void MojoToReport(const mojom::MemoryLeakReport& mojo_report, + MemoryLeakReportProto* report) { + report->set_size_bytes(mojo_report.size_bytes); + for (auto call_stack_addr : mojo_report.call_stack) + report->add_call_stack(call_stack_addr); + + for (const auto& history_entry : mojo_report.alloc_breakdown_history) { + auto proto_entry = report->add_alloc_breakdown_history(); + for (auto count : history_entry->counts_by_size) { + proto_entry->add_counts_by_size(count); + } + proto_entry->set_count_for_call_stack(history_entry->count_for_call_stack); + } +} + +} // namespace protobuf_to_mojo_converter +} // namespace leak_detector +} // namespace metrics diff --git a/chromium/components/metrics/leak_detector/protobuf_to_mojo_converter.h b/chromium/components/metrics/leak_detector/protobuf_to_mojo_converter.h new file mode 100644 index 00000000000..9447f76ffab --- /dev/null +++ b/chromium/components/metrics/leak_detector/protobuf_to_mojo_converter.h @@ -0,0 +1,36 @@ +// Copyright 2016 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. + +#ifndef COMPONENTS_METRICS_LEAK_DETECTOR_PROTOBUF_TO_MOJO_CONVERTER_H_ +#define COMPONENTS_METRICS_LEAK_DETECTOR_PROTOBUF_TO_MOJO_CONVERTER_H_ + +#include "components/metrics/leak_detector/leak_detector.mojom.h" +#include "components/metrics/proto/memory_leak_report.pb.h" + +namespace metrics { +namespace leak_detector { +namespace protobuf_to_mojo_converter { + +// Converts between a MemoryLeakReportProto::Params protobuf and a +// mojom::LeakDetectorParams Mojo structure. The Mojo structure must already be +// allocated. +void ParamsToMojo(const MemoryLeakReportProto::Params& params, + mojom::LeakDetectorParams* mojo_params); +void MojoToParams(const mojom::LeakDetectorParams& mojo_params, + MemoryLeakReportProto::Params* params); + +// Converts between a MemoryLeakReportProto protobuf and a +// mojom::MemoryLeakReport Mojo structure. The Mojo structure must already be +// allocated. The conversion only covers the fields that are filled in by the +// LeakDetector class. +void ReportToMojo(const MemoryLeakReportProto& report, + mojom::MemoryLeakReport* mojo_report); +void MojoToReport(const mojom::MemoryLeakReport& mojo_report, + MemoryLeakReportProto* report); + +} // namespace protobuf_to_mojo_converter +} // namespace leak_detector +} // namespace metrics + +#endif // COMPONENTS_METRICS_LEAK_DETECTOR_PROTOBUF_TO_MOJO_CONVERTER_H_ diff --git a/chromium/components/metrics/leak_detector/protobuf_to_mojo_converter_unittest.cc b/chromium/components/metrics/leak_detector/protobuf_to_mojo_converter_unittest.cc new file mode 100644 index 00000000000..f768c312f05 --- /dev/null +++ b/chromium/components/metrics/leak_detector/protobuf_to_mojo_converter_unittest.cc @@ -0,0 +1,139 @@ +// Copyright 2016 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/leak_detector/protobuf_to_mojo_converter.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace metrics { +namespace leak_detector { + +TEST(protobuf_to_mojo_converterTest, ConvertParams) { + MemoryLeakReportProto::Params params; + params.set_sampling_rate(0.75); + params.set_max_stack_depth(19); + params.set_analysis_interval_bytes(25 * 1024 * 1024); + params.set_size_suspicion_threshold(8); + params.set_call_stack_suspicion_threshold(11); + + // Convert to equivalent Mojo struct. + mojo::StructPtr<mojom::LeakDetectorParams> mojo_params = + mojom::LeakDetectorParams::New(); + protobuf_to_mojo_converter::ParamsToMojo(params, mojo_params.get()); + + EXPECT_DOUBLE_EQ(0.75, mojo_params->sampling_rate); + EXPECT_EQ(19U, mojo_params->max_stack_depth); + EXPECT_EQ(25U * 1024 * 1024, mojo_params->analysis_interval_bytes); + EXPECT_EQ(8U, mojo_params->size_suspicion_threshold); + EXPECT_EQ(11U, mojo_params->call_stack_suspicion_threshold); + + // Convert Mojo struct back to protobuf. + MemoryLeakReportProto::Params new_params; + protobuf_to_mojo_converter::MojoToParams(*mojo_params, &new_params); + + EXPECT_DOUBLE_EQ(0.75, new_params.sampling_rate()); + EXPECT_EQ(19U, new_params.max_stack_depth()); + EXPECT_EQ(25U * 1024 * 1024, new_params.analysis_interval_bytes()); + EXPECT_EQ(8U, new_params.size_suspicion_threshold()); + EXPECT_EQ(11U, new_params.call_stack_suspicion_threshold()); +} + +TEST(protobuf_to_mojo_converterTest, ConvertReport) { + MemoryLeakReportProto report; + report.add_call_stack(0xdeadbeef); + report.add_call_stack(0xc001d00d); + report.add_call_stack(0x900df00d); + report.set_size_bytes(24); + + auto entry1 = report.add_alloc_breakdown_history(); + entry1->add_counts_by_size(1); + entry1->add_counts_by_size(2); + entry1->add_counts_by_size(3); + entry1->set_count_for_call_stack(4); + + auto entry2 = report.add_alloc_breakdown_history(); + entry2->add_counts_by_size(11); + entry2->add_counts_by_size(12); + entry2->add_counts_by_size(13); + entry2->add_counts_by_size(14); + entry2->set_count_for_call_stack(15); + + auto entry3 = report.add_alloc_breakdown_history(); + entry3->add_counts_by_size(21); + entry3->add_counts_by_size(22); + entry3->add_counts_by_size(23); + entry3->add_counts_by_size(24); + entry3->add_counts_by_size(25); + entry3->set_count_for_call_stack(26); + + // Convert to equivalent Mojo struct. + mojo::StructPtr<mojom::MemoryLeakReport> mojo_report = + mojom::MemoryLeakReport::New(); + protobuf_to_mojo_converter::ReportToMojo(report, mojo_report.get()); + + ASSERT_EQ(3U, mojo_report->call_stack.size()); + EXPECT_EQ(0xdeadbeef, mojo_report->call_stack[0]); + EXPECT_EQ(0xc001d00d, mojo_report->call_stack[1]); + EXPECT_EQ(0x900df00d, mojo_report->call_stack[2]); + EXPECT_EQ(24U, mojo_report->size_bytes); + + ASSERT_EQ(3U, mojo_report->alloc_breakdown_history.size()); + + ASSERT_EQ(3U, mojo_report->alloc_breakdown_history[0]->counts_by_size.size()); + EXPECT_EQ(1U, mojo_report->alloc_breakdown_history[0]->counts_by_size[0]); + EXPECT_EQ(2U, mojo_report->alloc_breakdown_history[0]->counts_by_size[1]); + EXPECT_EQ(3U, mojo_report->alloc_breakdown_history[0]->counts_by_size[2]); + EXPECT_EQ(4U, mojo_report->alloc_breakdown_history[0]->count_for_call_stack); + + ASSERT_EQ(4U, mojo_report->alloc_breakdown_history[1]->counts_by_size.size()); + EXPECT_EQ(11U, mojo_report->alloc_breakdown_history[1]->counts_by_size[0]); + EXPECT_EQ(12U, mojo_report->alloc_breakdown_history[1]->counts_by_size[1]); + EXPECT_EQ(13U, mojo_report->alloc_breakdown_history[1]->counts_by_size[2]); + EXPECT_EQ(14U, mojo_report->alloc_breakdown_history[1]->counts_by_size[3]); + EXPECT_EQ(15U, mojo_report->alloc_breakdown_history[1]->count_for_call_stack); + + ASSERT_EQ(5U, mojo_report->alloc_breakdown_history[2]->counts_by_size.size()); + EXPECT_EQ(21U, mojo_report->alloc_breakdown_history[2]->counts_by_size[0]); + EXPECT_EQ(22U, mojo_report->alloc_breakdown_history[2]->counts_by_size[1]); + EXPECT_EQ(23U, mojo_report->alloc_breakdown_history[2]->counts_by_size[2]); + EXPECT_EQ(24U, mojo_report->alloc_breakdown_history[2]->counts_by_size[3]); + EXPECT_EQ(25U, mojo_report->alloc_breakdown_history[2]->counts_by_size[4]); + EXPECT_EQ(26U, mojo_report->alloc_breakdown_history[2]->count_for_call_stack); + + // Convert Mojo struct back to protobuf. + MemoryLeakReportProto new_report; + protobuf_to_mojo_converter::MojoToReport(*mojo_report, &new_report); + + ASSERT_EQ(3, new_report.call_stack().size()); + EXPECT_EQ(0xdeadbeef, new_report.call_stack(0)); + EXPECT_EQ(0xc001d00d, new_report.call_stack(1)); + EXPECT_EQ(0x900df00d, new_report.call_stack(2)); + EXPECT_EQ(24U, new_report.size_bytes()); + + ASSERT_EQ(3, new_report.alloc_breakdown_history().size()); + + ASSERT_EQ(3, new_report.alloc_breakdown_history(0).counts_by_size().size()); + EXPECT_EQ(1U, new_report.alloc_breakdown_history(0).counts_by_size(0)); + EXPECT_EQ(2U, new_report.alloc_breakdown_history(0).counts_by_size(1)); + EXPECT_EQ(3U, new_report.alloc_breakdown_history(0).counts_by_size(2)); + EXPECT_EQ(4U, new_report.alloc_breakdown_history(0).count_for_call_stack()); + + ASSERT_EQ(4, new_report.alloc_breakdown_history(1).counts_by_size().size()); + EXPECT_EQ(11U, new_report.alloc_breakdown_history(1).counts_by_size(0)); + EXPECT_EQ(12U, new_report.alloc_breakdown_history(1).counts_by_size(1)); + EXPECT_EQ(13U, new_report.alloc_breakdown_history(1).counts_by_size(2)); + EXPECT_EQ(14U, new_report.alloc_breakdown_history(1).counts_by_size(3)); + EXPECT_EQ(15U, new_report.alloc_breakdown_history(1).count_for_call_stack()); + + ASSERT_EQ(5, new_report.alloc_breakdown_history(2).counts_by_size().size()); + EXPECT_EQ(21U, new_report.alloc_breakdown_history(2).counts_by_size(0)); + EXPECT_EQ(22U, new_report.alloc_breakdown_history(2).counts_by_size(1)); + EXPECT_EQ(23U, new_report.alloc_breakdown_history(2).counts_by_size(2)); + EXPECT_EQ(24U, new_report.alloc_breakdown_history(2).counts_by_size(3)); + EXPECT_EQ(25U, new_report.alloc_breakdown_history(2).counts_by_size(4)); + EXPECT_EQ(26U, new_report.alloc_breakdown_history(2).count_for_call_stack()); +} + +} // namespace leak_detector +} // namespace metrics diff --git a/chromium/components/metrics/metrics_log.cc b/chromium/components/metrics/metrics_log.cc index 3d8cc1886a8..3824762e217 100644 --- a/chromium/components/metrics/metrics_log.cc +++ b/chromium/components/metrics/metrics_log.cc @@ -117,9 +117,9 @@ MetricsLog::~MetricsLog() { // static void MetricsLog::RegisterPrefs(PrefRegistrySimple* registry) { - registry->RegisterIntegerPref(prefs::kStabilityLaunchCount, 0); registry->RegisterIntegerPref(prefs::kStabilityCrashCount, 0); registry->RegisterIntegerPref(prefs::kStabilityIncompleteSessionEndCount, 0); + registry->RegisterIntegerPref(prefs::kStabilityLaunchCount, 0); registry->RegisterIntegerPref(prefs::kStabilityBreakpadRegistrationFail, 0); registry->RegisterIntegerPref( prefs::kStabilityBreakpadRegistrationSuccess, 0); @@ -129,6 +129,9 @@ void MetricsLog::RegisterPrefs(PrefRegistrySimple* registry) { std::string()); registry->RegisterStringPref(prefs::kStabilitySavedSystemProfileHash, std::string()); + registry->RegisterIntegerPref(prefs::kStabilityDeferredCount, 0); + registry->RegisterIntegerPref(prefs::kStabilityDiscardCount, 0); + registry->RegisterIntegerPref(prefs::kStabilityVersionMismatchCount, 0); } // static @@ -203,37 +206,70 @@ void MetricsLog::RecordStabilityMetrics( metrics_providers[i]->ProvideStabilityMetrics(system_profile); } - // Omit some stats unless this is the initial stability log. - if (log_type() != INITIAL_STABILITY_LOG) - return; + SystemProfileProto::Stability* stability = + system_profile->mutable_stability(); int incomplete_shutdown_count = pref->GetInteger(prefs::kStabilityIncompleteSessionEndCount); - pref->SetInteger(prefs::kStabilityIncompleteSessionEndCount, 0); + if (incomplete_shutdown_count) { + pref->SetInteger(prefs::kStabilityIncompleteSessionEndCount, 0); + stability->set_incomplete_shutdown_count(incomplete_shutdown_count); + } + int breakpad_registration_success_count = pref->GetInteger(prefs::kStabilityBreakpadRegistrationSuccess); - pref->SetInteger(prefs::kStabilityBreakpadRegistrationSuccess, 0); + if (breakpad_registration_success_count) { + pref->SetInteger(prefs::kStabilityBreakpadRegistrationSuccess, 0); + stability->set_breakpad_registration_success_count( + breakpad_registration_success_count); + } + int breakpad_registration_failure_count = pref->GetInteger(prefs::kStabilityBreakpadRegistrationFail); - pref->SetInteger(prefs::kStabilityBreakpadRegistrationFail, 0); + if (breakpad_registration_failure_count) { + pref->SetInteger(prefs::kStabilityBreakpadRegistrationFail, 0); + stability->set_breakpad_registration_failure_count( + breakpad_registration_failure_count); + } + int debugger_present_count = pref->GetInteger(prefs::kStabilityDebuggerPresent); - pref->SetInteger(prefs::kStabilityDebuggerPresent, 0); + if (debugger_present_count) { + pref->SetInteger(prefs::kStabilityDebuggerPresent, 0); + stability->set_debugger_present_count(debugger_present_count); + } + int debugger_not_present_count = pref->GetInteger(prefs::kStabilityDebuggerNotPresent); - pref->SetInteger(prefs::kStabilityDebuggerNotPresent, 0); + if (debugger_not_present_count) { + pref->SetInteger(prefs::kStabilityDebuggerNotPresent, 0); + stability->set_debugger_not_present_count(debugger_not_present_count); + } - // TODO(jar): The following are all optional, so we *could* optimize them for - // values of zero (and not include them). - SystemProfileProto::Stability* stability = - system_profile->mutable_stability(); - stability->set_incomplete_shutdown_count(incomplete_shutdown_count); - stability->set_breakpad_registration_success_count( - breakpad_registration_success_count); - stability->set_breakpad_registration_failure_count( - breakpad_registration_failure_count); - stability->set_debugger_present_count(debugger_present_count); - stability->set_debugger_not_present_count(debugger_not_present_count); + // Note: only logging the following histograms for non-zero values. + + int deferred_count = pref->GetInteger(prefs::kStabilityDeferredCount); + if (deferred_count) { + local_state_->SetInteger(prefs::kStabilityDeferredCount, 0); + UMA_STABILITY_HISTOGRAM_COUNTS_100( + "Stability.Internals.InitialStabilityLogDeferredCount", deferred_count); + } + + int discard_count = local_state_->GetInteger(prefs::kStabilityDiscardCount); + if (discard_count) { + local_state_->SetInteger(prefs::kStabilityDiscardCount, 0); + UMA_STABILITY_HISTOGRAM_COUNTS_100("Stability.Internals.DataDiscardCount", + discard_count); + } + + int version_mismatch_count = + local_state_->GetInteger(prefs::kStabilityVersionMismatchCount); + if (version_mismatch_count) { + local_state_->SetInteger(prefs::kStabilityVersionMismatchCount, 0); + UMA_STABILITY_HISTOGRAM_COUNTS_100( + "Stability.Internals.VersionMismatchCount", + version_mismatch_count); + } } void MetricsLog::RecordGeneralMetrics( @@ -285,9 +321,11 @@ bool MetricsLog::HasStabilityMetrics() const { // protobufs is complete. void MetricsLog::WriteRequiredStabilityAttributes(PrefService* pref) { int launch_count = pref->GetInteger(prefs::kStabilityLaunchCount); - pref->SetInteger(prefs::kStabilityLaunchCount, 0); + if (launch_count) + pref->SetInteger(prefs::kStabilityLaunchCount, 0); int crash_count = pref->GetInteger(prefs::kStabilityCrashCount); - pref->SetInteger(prefs::kStabilityCrashCount, 0); + if (crash_count) + pref->SetInteger(prefs::kStabilityCrashCount, 0); SystemProfileProto::Stability* stability = uma_proto()->mutable_system_profile()->mutable_stability(); @@ -379,19 +417,22 @@ void MetricsLog::RecordEnvironment( for (size_t i = 0; i < metrics_providers.size(); ++i) metrics_providers[i]->ProvideSystemProfileMetrics(system_profile); - std::string serialied_system_profile; + std::string serialized_system_profile; std::string base64_system_profile; - if (system_profile->SerializeToString(&serialied_system_profile)) { - base::Base64Encode(serialied_system_profile, &base64_system_profile); + if (system_profile->SerializeToString(&serialized_system_profile)) { + base::Base64Encode(serialized_system_profile, &base64_system_profile); PrefService* local_state = local_state_; local_state->SetString(prefs::kStabilitySavedSystemProfile, base64_system_profile); local_state->SetString(prefs::kStabilitySavedSystemProfileHash, - ComputeSHA1(serialied_system_profile)); + ComputeSHA1(serialized_system_profile)); } } -bool MetricsLog::LoadSavedEnvironmentFromPrefs() { +bool MetricsLog::LoadSavedEnvironmentFromPrefs(std::string* app_version) { + DCHECK(app_version); + app_version->clear(); + PrefService* local_state = local_state_; const std::string base64_system_profile = local_state->GetString(prefs::kStabilitySavedSystemProfile); @@ -404,10 +445,15 @@ bool MetricsLog::LoadSavedEnvironmentFromPrefs() { local_state->ClearPref(prefs::kStabilitySavedSystemProfileHash); SystemProfileProto* system_profile = uma_proto()->mutable_system_profile(); - std::string serialied_system_profile; - return base::Base64Decode(base64_system_profile, &serialied_system_profile) && - ComputeSHA1(serialied_system_profile) == system_profile_hash && - system_profile->ParseFromString(serialied_system_profile); + std::string serialized_system_profile; + + bool success = + base::Base64Decode(base64_system_profile, &serialized_system_profile) && + ComputeSHA1(serialized_system_profile) == system_profile_hash && + system_profile->ParseFromString(serialized_system_profile); + if (success) + *app_version = system_profile->app_version(); + return success; } void MetricsLog::CloseLog() { diff --git a/chromium/components/metrics/metrics_log.h b/chromium/components/metrics/metrics_log.h index 0506df28db0..6b37e7b4c98 100644 --- a/chromium/components/metrics/metrics_log.h +++ b/chromium/components/metrics/metrics_log.h @@ -100,9 +100,11 @@ class MetricsLog { int64_t metrics_reporting_enabled_date); // Loads the environment proto that was saved by the last RecordEnvironment() - // call from prefs and clears the pref value. Returns true on success or false - // if there was no saved environment in prefs or it could not be decoded. - bool LoadSavedEnvironmentFromPrefs(); + // call from prefs and clears the pref value. On success, returns true and + // |app_version| contains the recovered version. Otherwise (if there was no + // saved environment in prefs or it could not be decoded), returns false and + // |app_version| is empty. + bool LoadSavedEnvironmentFromPrefs(std::string* app_version); // Writes application stability metrics, including stability metrics provided // by the specified set of |metrics_providers|. The system profile portion of diff --git a/chromium/components/metrics/metrics_log_manager.cc b/chromium/components/metrics/metrics_log_manager.cc index 415db785656..d06b5ae0ada 100644 --- a/chromium/components/metrics/metrics_log_manager.cc +++ b/chromium/components/metrics/metrics_log_manager.cc @@ -42,11 +42,13 @@ MetricsLogManager::MetricsLogManager(PrefService* local_state, : unsent_logs_loaded_(false), initial_log_queue_(local_state, prefs::kMetricsInitialLogs, + prefs::kDeprecatedMetricsInitialLogs, kInitialLogsPersistLimit, kStorageByteLimitPerLogType, 0), ongoing_log_queue_(local_state, prefs::kMetricsOngoingLogs, + prefs::kDeprecatedMetricsOngoingLogs, kOngoingLogsPersistLimit, kStorageByteLimitPerLogType, max_ongoing_log_size) {} diff --git a/chromium/components/metrics/metrics_log_manager_unittest.cc b/chromium/components/metrics/metrics_log_manager_unittest.cc index 7a634609a54..fbd1dd5d2b5 100644 --- a/chromium/components/metrics/metrics_log_manager_unittest.cc +++ b/chromium/components/metrics/metrics_log_manager_unittest.cc @@ -37,7 +37,7 @@ class TestLogPrefService : public TestingPrefServiceSimple { list_length = GetList(prefs::kMetricsInitialLogs)->GetSize(); else list_length = GetList(prefs::kMetricsOngoingLogs)->GetSize(); - return list_length / 2; + return list_length; } }; @@ -133,11 +133,11 @@ TEST(MetricsLogManagerTest, InterjectedLogPreservesType) { MetricsLogManager log_manager(&pref_service, 0); log_manager.LoadPersistedUnsentLogs(); - log_manager.BeginLoggingWithLog(base::WrapUnique(new MetricsLog( - "id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service))); + log_manager.BeginLoggingWithLog(base::MakeUnique<MetricsLog>( + "id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)); log_manager.PauseCurrentLog(); - log_manager.BeginLoggingWithLog(base::WrapUnique(new MetricsLog( - "id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service))); + log_manager.BeginLoggingWithLog(base::MakeUnique<MetricsLog>( + "id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service)); log_manager.FinishCurrentLog(); log_manager.ResumePausedLog(); log_manager.StageNextLogForUpload(); @@ -163,8 +163,8 @@ TEST(MetricsLogManagerTest, StoreAndLoad) { // Simulate a log having already been unsent from a previous session. { std::string log("proto"); - PersistedLogs ongoing_logs(&pref_service, prefs::kMetricsOngoingLogs, 1, - 1, 0); + PersistedLogs ongoing_logs(&pref_service, prefs::kMetricsOngoingLogs, + prefs::kDeprecatedMetricsOngoingLogs, 1, 1, 0); ongoing_logs.StoreLog(log); ongoing_logs.SerializeLogs(); } @@ -173,11 +173,11 @@ TEST(MetricsLogManagerTest, StoreAndLoad) { log_manager.LoadPersistedUnsentLogs(); EXPECT_TRUE(log_manager.has_unsent_logs()); - log_manager.BeginLoggingWithLog(base::WrapUnique(new MetricsLog( - "id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service))); + log_manager.BeginLoggingWithLog(base::MakeUnique<MetricsLog>( + "id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service)); log_manager.FinishCurrentLog(); - log_manager.BeginLoggingWithLog(base::WrapUnique(new MetricsLog( - "id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service))); + log_manager.BeginLoggingWithLog(base::MakeUnique<MetricsLog>( + "id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)); log_manager.StageNextLogForUpload(); log_manager.FinishCurrentLog(); @@ -234,8 +234,8 @@ TEST(MetricsLogManagerTest, StoreStagedLogTypes) { MetricsLogManager log_manager(&pref_service, 0); log_manager.LoadPersistedUnsentLogs(); - log_manager.BeginLoggingWithLog(base::WrapUnique(new MetricsLog( - "id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service))); + log_manager.BeginLoggingWithLog(base::MakeUnique<MetricsLog>( + "id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)); log_manager.FinishCurrentLog(); log_manager.StageNextLogForUpload(); log_manager.PersistUnsentLogs(); @@ -249,8 +249,8 @@ TEST(MetricsLogManagerTest, StoreStagedLogTypes) { MetricsLogManager log_manager(&pref_service, 0); log_manager.LoadPersistedUnsentLogs(); - log_manager.BeginLoggingWithLog(base::WrapUnique(new MetricsLog( - "id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service))); + log_manager.BeginLoggingWithLog(base::MakeUnique<MetricsLog>( + "id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service)); log_manager.FinishCurrentLog(); log_manager.StageNextLogForUpload(); log_manager.PersistUnsentLogs(); @@ -267,11 +267,11 @@ TEST(MetricsLogManagerTest, LargeLogDiscarding) { MetricsLogManager log_manager(&pref_service, 1); log_manager.LoadPersistedUnsentLogs(); - log_manager.BeginLoggingWithLog(base::WrapUnique(new MetricsLog( - "id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service))); + log_manager.BeginLoggingWithLog(base::MakeUnique<MetricsLog>( + "id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service)); log_manager.FinishCurrentLog(); - log_manager.BeginLoggingWithLog(base::WrapUnique(new MetricsLog( - "id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service))); + log_manager.BeginLoggingWithLog(base::MakeUnique<MetricsLog>( + "id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)); log_manager.FinishCurrentLog(); // Only the ongoing log should be written out, due to the threshold. @@ -289,11 +289,11 @@ TEST(MetricsLogManagerTest, DiscardOrder) { MetricsLogManager log_manager(&pref_service, 0); log_manager.LoadPersistedUnsentLogs(); - log_manager.BeginLoggingWithLog(base::WrapUnique(new MetricsLog( - "id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service))); + log_manager.BeginLoggingWithLog(base::MakeUnique<MetricsLog>( + "id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service)); log_manager.FinishCurrentLog(); - log_manager.BeginLoggingWithLog(base::WrapUnique(new MetricsLog( - "id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service))); + log_manager.BeginLoggingWithLog(base::MakeUnique<MetricsLog>( + "id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)); log_manager.StageNextLogForUpload(); log_manager.FinishCurrentLog(); log_manager.DiscardStagedLog(); diff --git a/chromium/components/metrics/metrics_log_unittest.cc b/chromium/components/metrics/metrics_log_unittest.cc index fe03ff8607c..57a1209f1ad 100644 --- a/chromium/components/metrics/metrics_log_unittest.cc +++ b/chromium/components/metrics/metrics_log_unittest.cc @@ -276,12 +276,15 @@ TEST_F(MetricsLogTest, LoadSavedEnvironmentFromPrefs) { prefs::kStabilitySavedSystemProfileHash; TestMetricsServiceClient client; + client.set_version_string("bogus version"); // The pref value is empty, so loading it from prefs should fail. { TestMetricsLog log( kClientId, kSessionId, MetricsLog::ONGOING_LOG, &client, &prefs_); - EXPECT_FALSE(log.LoadSavedEnvironmentFromPrefs()); + std::string app_version; + EXPECT_FALSE(log.LoadSavedEnvironmentFromPrefs(&app_version)); + EXPECT_TRUE(app_version.empty()); } // Do a RecordEnvironment() call and check whether the pref is recorded. @@ -298,7 +301,9 @@ TEST_F(MetricsLogTest, LoadSavedEnvironmentFromPrefs) { { TestMetricsLog log( kClientId, kSessionId, MetricsLog::ONGOING_LOG, &client, &prefs_); - EXPECT_TRUE(log.LoadSavedEnvironmentFromPrefs()); + std::string app_version; + EXPECT_TRUE(log.LoadSavedEnvironmentFromPrefs(&app_version)); + EXPECT_EQ("bogus version", app_version); // Check some values in the system profile. EXPECT_EQ(kInstallDateExpected, log.system_profile().install_date()); EXPECT_EQ(kEnabledDateExpected, log.system_profile().uma_enabled_date()); @@ -322,7 +327,9 @@ TEST_F(MetricsLogTest, LoadSavedEnvironmentFromPrefs) { prefs_.SetString(kSystemProfileHashPref, "deadbeef"); TestMetricsLog log( kClientId, kSessionId, MetricsLog::ONGOING_LOG, &client, &prefs_); - EXPECT_FALSE(log.LoadSavedEnvironmentFromPrefs()); + std::string app_version; + EXPECT_FALSE(log.LoadSavedEnvironmentFromPrefs(&app_version)); + EXPECT_TRUE(app_version.empty()); // Ensure that the prefs are cleared, even if the call failed. EXPECT_TRUE(prefs_.GetString(kSystemProfilePref).empty()); EXPECT_TRUE(prefs_.GetString(kSystemProfileHashPref).empty()); @@ -388,12 +395,12 @@ TEST_F(MetricsLogTest, InitialLogStabilityMetrics) { // Required metrics: EXPECT_TRUE(stability.has_launch_count()); EXPECT_TRUE(stability.has_crash_count()); - // Initial log metrics: - EXPECT_TRUE(stability.has_incomplete_shutdown_count()); - EXPECT_TRUE(stability.has_breakpad_registration_success_count()); - EXPECT_TRUE(stability.has_breakpad_registration_failure_count()); - EXPECT_TRUE(stability.has_debugger_present_count()); - EXPECT_TRUE(stability.has_debugger_not_present_count()); + // Initial log metrics: only expected if non-zero. + EXPECT_FALSE(stability.has_incomplete_shutdown_count()); + EXPECT_FALSE(stability.has_breakpad_registration_success_count()); + EXPECT_FALSE(stability.has_breakpad_registration_failure_count()); + EXPECT_FALSE(stability.has_debugger_present_count()); + EXPECT_FALSE(stability.has_debugger_not_present_count()); // The test provider should have been called upon to provide initial // stability and regular stability metrics. @@ -418,7 +425,7 @@ TEST_F(MetricsLogTest, OngoingLogStabilityMetrics) { // Required metrics: EXPECT_TRUE(stability.has_launch_count()); EXPECT_TRUE(stability.has_crash_count()); - // Initial log metrics: + // Initial log metrics: only expected if non-zero. EXPECT_FALSE(stability.has_incomplete_shutdown_count()); EXPECT_FALSE(stability.has_breakpad_registration_success_count()); EXPECT_FALSE(stability.has_breakpad_registration_failure_count()); diff --git a/chromium/components/metrics/metrics_pref_names.cc b/chromium/components/metrics/metrics_pref_names.cc index b751c02d4f4..1d5a7b63e84 100644 --- a/chromium/components/metrics/metrics_pref_names.cc +++ b/chromium/components/metrics/metrics_pref_names.cc @@ -7,6 +7,21 @@ namespace metrics { namespace prefs { +// Array of strings that are each UMA logs that were supposed to be sent in the +// first minute of a browser session. These logs include things like crash count +// info, etc. +// Deprecated by kMetricsInitialLogs. +const char kDeprecatedMetricsInitialLogs[] = + "user_experience_metrics.initial_logs_list"; + +// Array of strings that are each UMA logs that were not sent because the +// browser terminated before these accumulated metrics could be sent. These +// logs typically include histograms and memory reports, as well as ongoing +// user activities. +// Deprecated by kMetricsOngoingLogs. +const char kDeprecatedMetricsOngoingLogs[] = + "user_experience_metrics.ongoing_logs_list"; + // Set once, to the current epoch time, on the first run of chrome on this // machine. Attached to metrics reports forever thereafter. const char kInstallDate[] = "uninstall_metrics.installation_date2"; @@ -23,11 +38,10 @@ const char kMetricsClientID[] = "user_experience_metrics.client_id2"; // used for the value is metrics::MetricsServiceClient::EnableMetricsDefault. const char kMetricsDefaultOptIn[] = "user_experience_metrics.default_opt_in"; -// Array of strings that are each UMA logs that were supposed to be sent in the -// first minute of a browser session. These logs include things like crash count -// info, etc. -const char kMetricsInitialLogs[] = - "user_experience_metrics.initial_logs_list"; +// Array of dictionaries that are each UMA logs that were supposed to be sent in +// the first minute of a browser session. These logs include things like crash +// count info, etc. +const char kMetricsInitialLogs[] = "user_experience_metrics.initial_logs2"; // The metrics entropy source. // Note: The name low_entropy_source2 is a result of creating @@ -39,12 +53,11 @@ const char kMetricsLowEntropySource[] = // stored locally and never transmitted in metrics reports. const char kMetricsMachineId[] = "user_experience_metrics.machine_id"; -// Array of strings that are each UMA logs that were not sent because the -// browser terminated before these accumulated metrics could be sent. These +// Array of dictionaries that are each UMA logs that were not sent because the +// browser terminated before these accumulated metrics could be sent. These // logs typically include histograms and memory reports, as well as ongoing // user activities. -const char kMetricsOngoingLogs[] = - "user_experience_metrics.ongoing_logs_list"; +const char kMetricsOngoingLogs[] = "user_experience_metrics.ongoing_logs2"; // Boolean that indicates a cloned install has been detected and the metrics // client id and low entropy source should be reset. @@ -55,8 +68,8 @@ const char kMetricsResetIds[] = "user_experience_metrics.reset_metrics_ids"; const char kMetricsReportingEnabled[] = "user_experience_metrics.reporting_enabled"; -// Date/time when the user opted in to UMA and generated the client id for the -// very first time (local machine time, stored as a 64-bit time_t value). +// Date/time when the user opted in to UMA and generated the client id most +// recently (local machine time, stored as a 64-bit time_t value). const char kMetricsReportingEnabledTimestamp[] = "user_experience_metrics.client_id_timestamp"; @@ -86,6 +99,16 @@ const char kStabilityChildProcessCrashCount[] = const char kStabilityCrashCount[] = "user_experience_metrics.stability.crash_count"; +// Number of times the initial stability log upload was deferred to the next +// startup. +const char kStabilityDeferredCount[] = + "user_experience_metrics.stability.deferred_count"; + +// Number of times stability data was discarded. This is accumulated since the +// last report, even across versions. +const char kStabilityDiscardCount[] = + "user_experience_metrics.stability.discard_count"; + // Number of times the browser has been run under a debugger. const char kStabilityDebuggerPresent[] = "user_experience_metrics.stability.debugger_present"; @@ -111,6 +134,11 @@ const char kStabilityExtensionRendererCrashCount[] = const char kStabilityExtensionRendererFailedLaunchCount[] = "user_experience_metrics.stability.extension_renderer_failed_launch_count"; +// Number of times an extension renderer process successfully launched since the +// last report. +const char kStabilityExtensionRendererLaunchCount[] = + "user_experience_metrics.stability.extension_renderer_launch_count"; + // Number of times the session end did not complete. const char kStabilityIncompleteSessionEndCount[] = "user_experience_metrics.stability.incomplete_session_end_count"; @@ -145,6 +173,11 @@ const char kStabilityRendererFailedLaunchCount[] = const char kStabilityRendererHangCount[] = "user_experience_metrics.stability.renderer_hang_count"; +// Number of times a renderer process successfully launched since the last +// report. +const char kStabilityRendererLaunchCount[] = + "user_experience_metrics.stability.renderer_launch_count"; + // Base64 encoded serialized UMA system profile proto from the previous session. const char kStabilitySavedSystemProfile[] = "user_experience_metrics.stability.saved_system_profile"; @@ -168,6 +201,11 @@ const char kStabilityStatsBuildTime[] = const char kStabilityStatsVersion[] = "user_experience_metrics.stability.stats_version"; +// Number of times the version number stored in prefs did not match the +// serialized system profile version number. +const char kStabilityVersionMismatchCount[] = + "user_experience_metrics.stability.version_mismatch_count"; + // The keys below are strictly increasing counters over the lifetime of // a chrome installation. They are (optionally) sent up to the uninstall // survey in the event of uninstallation. diff --git a/chromium/components/metrics/metrics_pref_names.h b/chromium/components/metrics/metrics_pref_names.h index bcd531892d8..df25e3854a9 100644 --- a/chromium/components/metrics/metrics_pref_names.h +++ b/chromium/components/metrics/metrics_pref_names.h @@ -10,6 +10,8 @@ namespace prefs { // Alphabetical list of preference names specific to the metrics // component. Document each in the .cc file. +extern const char kDeprecatedMetricsInitialLogs[]; +extern const char kDeprecatedMetricsOngoingLogs[]; extern const char kInstallDate[]; extern const char kMetricsClientID[]; extern const char kMetricsDefaultOptIn[]; @@ -34,9 +36,12 @@ extern const char kStabilityChildProcessCrashCount[]; extern const char kStabilityCrashCount[]; extern const char kStabilityDebuggerPresent[]; extern const char kStabilityDebuggerNotPresent[]; +extern const char kStabilityDeferredCount[]; +extern const char kStabilityDiscardCount[]; extern const char kStabilityExecutionPhase[]; extern const char kStabilityExtensionRendererCrashCount[]; extern const char kStabilityExtensionRendererFailedLaunchCount[]; +extern const char kStabilityExtensionRendererLaunchCount[]; extern const char kStabilityExitedCleanly[]; extern const char kStabilityIncompleteSessionEndCount[]; extern const char kStabilityLastTimestampSec[]; @@ -46,11 +51,13 @@ extern const char kStabilityPageLoadCount[]; extern const char kStabilityRendererCrashCount[]; extern const char kStabilityRendererFailedLaunchCount[]; extern const char kStabilityRendererHangCount[]; +extern const char kStabilityRendererLaunchCount[]; extern const char kStabilitySavedSystemProfile[]; extern const char kStabilitySavedSystemProfileHash[]; extern const char kStabilitySessionEndCompleted[]; extern const char kStabilityStatsBuildTime[]; extern const char kStabilityStatsVersion[]; +extern const char kStabilityVersionMismatchCount[]; extern const char kUninstallLaunchCount[]; extern const char kUninstallMetricsPageLoadCount[]; extern const char kUninstallMetricsUptimeSec[]; diff --git a/chromium/components/metrics/metrics_provider.cc b/chromium/components/metrics/metrics_provider.cc index 924cc6f3180..50f621a73ec 100644 --- a/chromium/components/metrics/metrics_provider.cc +++ b/chromium/components/metrics/metrics_provider.cc @@ -24,6 +24,9 @@ void MetricsProvider::OnRecordingEnabled() { void MetricsProvider::OnRecordingDisabled() { } +void MetricsProvider::OnAppEnterBackground() { +} + void MetricsProvider::ProvideSystemProfileMetrics( SystemProfileProto* system_profile_proto) { } diff --git a/chromium/components/metrics/metrics_provider.h b/chromium/components/metrics/metrics_provider.h index 6a690f5251c..d76b6772ccd 100644 --- a/chromium/components/metrics/metrics_provider.h +++ b/chromium/components/metrics/metrics_provider.h @@ -36,6 +36,13 @@ class MetricsProvider { // Called when metrics recording has been disabled. virtual void OnRecordingDisabled(); + // Called when the application is going into background mode, on platforms + // where applications may be killed when going into the background (Android, + // iOS). Providers that buffer histogram data in memory should persist + // histograms in this callback, as the application may be killed without + // further notification after this callback. + virtual void OnAppEnterBackground(); + // Provides additional metrics into the system profile. virtual void ProvideSystemProfileMetrics( SystemProfileProto* system_profile_proto); diff --git a/chromium/components/metrics/metrics_service.cc b/chromium/components/metrics/metrics_service.cc index a14b047a6c2..2acc9cb0b88 100644 --- a/chromium/components/metrics/metrics_service.cc +++ b/chromium/components/metrics/metrics_service.cc @@ -361,13 +361,6 @@ bool MetricsService::WasLastShutdownClean() const { return clean_exit_beacon_.exited_cleanly(); } -std::unique_ptr<const base::FieldTrial::EntropyProvider> -MetricsService::CreateEntropyProvider() { - // TODO(asvitkine): Refactor the code so that MetricsService does not expose - // this method. - return state_manager_->CreateDefaultEntropyProvider(); -} - void MetricsService::EnableRecording() { DCHECK(IsSingleThreaded()); @@ -396,8 +389,6 @@ void MetricsService::DisableRecording() { return; recording_state_ = INACTIVE; - client_->OnRecordingDisabled(); - base::RemoveActionCallback(action_callback_); for (MetricsProvider* provider : metrics_providers_) @@ -469,6 +460,11 @@ void MetricsService::OnAppEnterBackground() { MarkAppCleanShutdownAndCommit(&clean_exit_beacon_, local_state_); + // Give providers a chance to persist histograms as part of being + // backgrounded. + for (MetricsProvider* provider : metrics_providers_) + provider->OnAppEnterBackground(); + // At this point, there's no way of knowing when the process will be // killed, so this has to be treated similar to a shutdown, closing and // persisting all logs. Unlinke a shutdown, the state is primed to be ready @@ -520,12 +516,20 @@ void MetricsService::ClearSavedStabilityMetrics() { provider->ClearSavedStabilityMetrics(); // Reset the prefs that are managed by MetricsService/MetricsLog directly. + local_state_->SetInteger(prefs::kStabilityBreakpadRegistrationSuccess, 0); + local_state_->SetInteger(prefs::kStabilityBreakpadRegistrationFail, 0); local_state_->SetInteger(prefs::kStabilityCrashCount, 0); + local_state_->SetInteger(prefs::kStabilityDebuggerPresent, 0); + local_state_->SetInteger(prefs::kStabilityDebuggerNotPresent, 0); local_state_->SetInteger(prefs::kStabilityExecutionPhase, UNINITIALIZED_PHASE); local_state_->SetInteger(prefs::kStabilityIncompleteSessionEndCount, 0); local_state_->SetInteger(prefs::kStabilityLaunchCount, 0); local_state_->SetBoolean(prefs::kStabilitySessionEndCompleted, true); + local_state_->SetInteger(prefs::kStabilityDeferredCount, 0); + // Note: kStabilityDiscardCount is not cleared as its intent is to measure + // the number of times data is discarded, even across versions. + local_state_->SetInteger(prefs::kStabilityVersionMismatchCount, 0); } void MetricsService::PushExternalLog(const std::string& log) { @@ -542,6 +546,11 @@ UpdateUsagePrefCallbackType MetricsService::GetDataUseForwardingCallback() { return UpdateUsagePrefCallbackType(); } +void MetricsService::MergeHistogramDeltas() { + for (MetricsProvider* provider : metrics_providers_) + provider->MergeHistogramDeltas(); +} + //------------------------------------------------------------------------------ // private methods //------------------------------------------------------------------------------ @@ -554,8 +563,11 @@ void MetricsService::InitializeMetricsState() { const int64_t buildtime = MetricsLog::GetBuildTime(); const std::string version = client_->GetVersionString(); bool version_changed = false; - if (local_state_->GetInt64(prefs::kStabilityStatsBuildTime) != buildtime || - local_state_->GetString(prefs::kStabilityStatsVersion) != version) { + int64_t previous_buildtime = + local_state_->GetInt64(prefs::kStabilityStatsBuildTime); + std::string previous_version = + local_state_->GetString(prefs::kStabilityStatsVersion); + if (previous_buildtime != buildtime || previous_version != version) { local_state_->SetString(prefs::kStabilityStatsVersion, version); local_state_->SetInt64(prefs::kStabilityStatsBuildTime, buildtime); version_changed = true; @@ -587,8 +599,11 @@ void MetricsService::InitializeMetricsState() { // If the previous session didn't exit cleanly, or if any provider // explicitly requests it, prepare an initial stability log - // provided UMA is enabled. - if (state_manager_->IsMetricsReportingEnabled()) - has_initial_stability_log = PrepareInitialStabilityLog(); + if (state_manager_->IsMetricsReportingEnabled()) { + has_initial_stability_log = PrepareInitialStabilityLog(previous_version); + if (!has_initial_stability_log) + IncrementPrefValue(prefs::kStabilityDeferredCount); + } } // If no initial stability log was generated and there was a version upgrade, @@ -597,8 +612,10 @@ void MetricsService::InitializeMetricsState() { // number of different edge cases, such as if the last version crashed before // it could save off a system profile or if UMA reporting is disabled (which // normally results in stats being accumulated). - if (!has_initial_stability_log && version_changed) + if (!has_initial_stability_log && version_changed) { ClearSavedStabilityMetrics(); + IncrementPrefValue(prefs::kStabilityDiscardCount); + } // Update session ID. ++session_id_; @@ -637,9 +654,6 @@ void MetricsService::InitializeMetricsState() { } void MetricsService::OnUserAction(const std::string& action) { - if (!ShouldLogEvents()) - return; - log_manager_.current_log()->RecordUserAction(action); HandleIdleSinceLastTransmission(false); } @@ -894,7 +908,8 @@ bool MetricsService::ProvidersHaveInitialStabilityMetrics() { return false; } -bool MetricsService::PrepareInitialStabilityLog() { +bool MetricsService::PrepareInitialStabilityLog( + const std::string& prefs_previous_version) { DCHECK_EQ(INITIALIZED, state_); std::unique_ptr<MetricsLog> initial_stability_log( @@ -902,9 +917,13 @@ bool MetricsService::PrepareInitialStabilityLog() { // Do not call NotifyOnDidCreateMetricsLog here because the stability // log describes stats from the _previous_ session. - - if (!initial_stability_log->LoadSavedEnvironmentFromPrefs()) + std::string system_profile_app_version; + if (!initial_stability_log->LoadSavedEnvironmentFromPrefs( + &system_profile_app_version)) { return false; + } + if (system_profile_app_version != prefs_previous_version) + IncrementPrefValue(prefs::kStabilityVersionMismatchCount); log_manager_.PauseCurrentLog(); log_manager_.BeginLoggingWithLog(std::move(initial_stability_log)); @@ -1070,6 +1089,33 @@ void MetricsService::RegisterSyntheticFieldTrial( NotifySyntheticTrialObservers(); } +void MetricsService::RegisterSyntheticMultiGroupFieldTrial( + uint32_t trial_name_hash, + const std::vector<uint32_t>& group_name_hashes) { + auto has_same_trial_name = + [trial_name_hash](const variations::SyntheticTrialGroup& x) { + return x.id.name == trial_name_hash; + }; + synthetic_trial_groups_.erase( + std::remove_if(synthetic_trial_groups_.begin(), + synthetic_trial_groups_.end(), has_same_trial_name), + synthetic_trial_groups_.end()); + + if (group_name_hashes.empty()) + return; + + variations::SyntheticTrialGroup trial_group(trial_name_hash, + group_name_hashes[0]); + trial_group.start_time = base::TimeTicks::Now(); + for (uint32_t group_name_hash : group_name_hashes) { + // Note: Adding the trial group will copy it, so this re-uses the same + // |trial_group| struct for convenience (e.g. so start_time's all match). + trial_group.id.group = group_name_hash; + synthetic_trial_groups_.push_back(trial_group); + } + NotifySyntheticTrialObservers(); +} + void MetricsService::GetCurrentSyntheticFieldTrialsForTesting( std::vector<variations::ActiveGroupId>* synthetic_trials) { GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(), synthetic_trials); @@ -1105,9 +1151,8 @@ void MetricsService::GetSyntheticFieldTrialsOlderThan( std::unique_ptr<MetricsLog> MetricsService::CreateLog( MetricsLog::LogType log_type) { - return base::WrapUnique(new MetricsLog(state_manager_->client_id(), - session_id_, log_type, client_, - local_state_)); + return base::MakeUnique<MetricsLog>(state_manager_->client_id(), session_id_, + log_type, client_, local_state_); } void MetricsService::RecordCurrentEnvironment(MetricsLog* log) { @@ -1121,32 +1166,24 @@ void MetricsService::RecordCurrentHistograms() { DCHECK(log_manager_.current_log()); SCOPED_UMA_HISTOGRAM_TIMER("UMA.MetricsService.RecordCurrentHistograms.Time"); - // Merge any data from metrics providers into the global StatisticsRecorder. - for (MetricsProvider* provider : metrics_providers_) - provider->MergeHistogramDeltas(); - - histogram_snapshot_manager_.StartDeltas(); // "true" to the begin() call indicates that StatisticsRecorder should include // histograms held in persistent storage. - histogram_snapshot_manager_.PrepareDeltasWithoutStartFinish( + histogram_snapshot_manager_.PrepareDeltas( base::StatisticsRecorder::begin(true), base::StatisticsRecorder::end(), base::Histogram::kNoFlags, base::Histogram::kUmaTargetedHistogramFlag); for (MetricsProvider* provider : metrics_providers_) provider->RecordHistogramSnapshots(&histogram_snapshot_manager_); - histogram_snapshot_manager_.FinishDeltas(); } void MetricsService::RecordCurrentStabilityHistograms() { DCHECK(log_manager_.current_log()); - histogram_snapshot_manager_.StartDeltas(); // "true" indicates that StatisticsRecorder should include histograms in // persistent storage. - histogram_snapshot_manager_.PrepareDeltasWithoutStartFinish( + histogram_snapshot_manager_.PrepareDeltas( base::StatisticsRecorder::begin(true), base::StatisticsRecorder::end(), base::Histogram::kNoFlags, base::Histogram::kUmaStabilityHistogramFlag); for (MetricsProvider* provider : metrics_providers_) provider->RecordInitialHistogramSnapshots(&histogram_snapshot_manager_); - histogram_snapshot_manager_.FinishDeltas(); } void MetricsService::LogCleanShutdown() { @@ -1160,13 +1197,6 @@ void MetricsService::LogCleanShutdown() { MetricsService::SHUTDOWN_COMPLETE); } -bool MetricsService::ShouldLogEvents() { - // We simply don't log events to UMA if there is a single incognito - // session visible. The problem is that we always notify using the original - // profile in order to simplify notification processing. - return !client_->IsOffTheRecordSessionActive(); -} - void MetricsService::RecordBooleanPrefValue(const char* path, bool value) { DCHECK(IsSingleThreaded()); local_state_->SetBoolean(path, value); diff --git a/chromium/components/metrics/metrics_service.h b/chromium/components/metrics/metrics_service.h index a1f4f63c5a6..3f19a5b3490 100644 --- a/chromium/components/metrics/metrics_service.h +++ b/chromium/components/metrics/metrics_service.h @@ -124,16 +124,6 @@ class MetricsService : public base::HistogramFlattener { // Returns true if the last session exited cleanly. bool WasLastShutdownClean() const; - // Returns the preferred entropy provider used to seed persistent activities - // based on whether or not metrics reporting will be permitted on this client. - // - // If metrics reporting is enabled, this method returns an entropy provider - // that has a high source of entropy, partially based on the client ID. - // Otherwise, it returns an entropy provider that is based on a low entropy - // source. - std::unique_ptr<const base::FieldTrial::EntropyProvider> - CreateEntropyProvider(); - // At startup, prefs needs to be called with a list of all the pref names and // types we'll be using. static void RegisterPrefs(PrefRegistrySimple* registry); @@ -220,6 +210,9 @@ class MetricsService : public base::HistogramFlattener { // from any thread, but this function should be called on UI thread. UpdateUsagePrefCallbackType GetDataUseForwardingCallback(); + // Merge any data from metrics providers into the global StatisticsRecorder. + void MergeHistogramDeltas(); + protected: // Exposed for testing. MetricsLogManager* log_manager() { return &log_manager_; } @@ -259,9 +252,18 @@ class MetricsService : public base::HistogramFlattener { // registered at a time for a given trial_name. Only the last group name that // is registered for a given trial name will be recorded. The values passed // in must not correspond to any real field trial in the code. + // Note: Should not be used to replace trials that were registered with + // RegisterMultiGroupSyntheticFieldTrial(). void RegisterSyntheticFieldTrial( const variations::SyntheticTrialGroup& trial_group); + // Similar to RegisterSyntheticFieldTrial(), but registers a synthetic trial + // that has multiple active groups for a given trial name hash. Any previous + // groups registered for |trial_name_hash| will be replaced. + void RegisterSyntheticMultiGroupFieldTrial( + uint32_t trial_name_hash, + const std::vector<uint32_t>& group_name_hashes); + // Calls into the client to initialize some system profile metrics. void StartInitTask(); @@ -340,9 +342,10 @@ class MetricsService : public base::HistogramFlattener { // Prepares the initial stability log, which is only logged when the previous // run of Chrome crashed. This log contains any stability metrics left over // from that previous run, and only these stability metrics. It uses the - // system profile from the previous session. Returns true if a log was - // created. - bool PrepareInitialStabilityLog(); + // system profile from the previous session. |prefs_previous_version| is used + // to validate the version number recovered from the system profile. Returns + // true if a log was created. + bool PrepareInitialStabilityLog(const std::string& prefs_previous_version); // Prepares the initial metrics log, which includes startup histograms and // profiler data, as well as incremental stability-related metrics. @@ -368,9 +371,6 @@ class MetricsService : public base::HistogramFlattener { // buffered plugin stability statistics. void RecordCurrentState(PrefService* pref); - // Checks whether events should currently be logged. - bool ShouldLogEvents(); - // Sets the value of the specified path in prefs and schedules a save. void RecordBooleanPrefValue(const char* path, bool value); @@ -482,6 +482,8 @@ class MetricsService : public base::HistogramFlattener { FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, PermutedEntropyCacheClearedWhenLowEntropyReset); FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, RegisterSyntheticTrial); + FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, + RegisterSyntheticMultiGroupFieldTrial); // Pointer used for obtaining data use pref updater callback on above layers. std::unique_ptr<DataUseTracker> data_use_tracker_; diff --git a/chromium/components/metrics/metrics_service_accessor.cc b/chromium/components/metrics/metrics_service_accessor.cc index ac04ba9884d..786895b40a1 100644 --- a/chromium/components/metrics/metrics_service_accessor.cc +++ b/chromium/components/metrics/metrics_service_accessor.cc @@ -16,20 +16,13 @@ namespace metrics { // static bool MetricsServiceAccessor::IsMetricsReportingEnabled( PrefService* pref_service) { - return IsMetricsReportingEnabledWithPrefValue( - pref_service->GetBoolean(prefs::kMetricsReportingEnabled)); -} - -// static -bool MetricsServiceAccessor::IsMetricsReportingEnabledWithPrefValue( - bool enabled_in_prefs) { #if defined(GOOGLE_CHROME_BUILD) // In official builds, disable metrics when reporting field trials are // forced; otherwise, use the value of the user's preference to determine // whether to enable metrics reporting. return !base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kForceFieldTrials) && - enabled_in_prefs; + pref_service->GetBoolean(prefs::kMetricsReportingEnabled); #else // In non-official builds, disable metrics reporting completely. return false; @@ -46,6 +39,19 @@ bool MetricsServiceAccessor::RegisterSyntheticFieldTrial( } // static +bool MetricsServiceAccessor::RegisterSyntheticMultiGroupFieldTrial( + MetricsService* metrics_service, + const std::string& trial_name, + const std::vector<uint32_t>& group_name_hashes) { + if (!metrics_service) + return false; + + metrics_service->RegisterSyntheticMultiGroupFieldTrial(HashName(trial_name), + group_name_hashes); + return true; +} + +// static bool MetricsServiceAccessor::RegisterSyntheticFieldTrialWithNameHash( MetricsService* metrics_service, uint32_t trial_name_hash, diff --git a/chromium/components/metrics/metrics_service_accessor.h b/chromium/components/metrics/metrics_service_accessor.h index 8d34675b5fe..c4b5e62f195 100644 --- a/chromium/components/metrics/metrics_service_accessor.h +++ b/chromium/components/metrics/metrics_service_accessor.h @@ -7,6 +7,7 @@ #include <stdint.h> #include <string> +#include <vector> #include "base/macros.h" @@ -28,32 +29,28 @@ class MetricsServiceAccessor { // Returns whether metrics reporting is enabled, using the value of the // kMetricsReportingEnabled pref in |pref_service| to determine whether user // has enabled reporting. - // NOTE: This method currently does not return the correct value on ChromeOS - // and Android due to http://crbug.com/362192 and http://crbug.com/532084. See - // ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled(). static bool IsMetricsReportingEnabled(PrefService* pref_service); - // Returns whether metrics reporting is enabled, using the value of - // |enabled_in_prefs| to determine whether the user has enabled reporting. - // Exists because kMetricsReportingEnabled is currently not used on all - // platforms. - // TODO(gayane): Consolidate metric prefs on all platforms and eliminate this - // method. http://crbug.com/362192, http://crbug.com/532084 - static bool IsMetricsReportingEnabledWithPrefValue(bool enabled_in_prefs); // Registers a field trial name and group with |metrics_service| (if not // null), to be used to annotate a UMA report with a particular configuration - // state. A UMA report will be annotated with this trial group if and only if - // all events in the report were created after the trial is registered. Only - // one group name may be registered at a time for a given trial name. Only the - // last group name that is registered for a given trial name will be recorded. - // The values passed in must not correspond to any real field trial in the - // code. Returns true on success. - // See the comment on MetricsService::RegisterSyntheticFieldTrial for details. + // state. Returns true on success. + // See the comment on MetricsService::RegisterSyntheticFieldTrial() for + // details. static bool RegisterSyntheticFieldTrial(MetricsService* metrics_service, const std::string& trial_name, const std::string& group_name); + // Registers a field trial name and set of groups with |metrics_service| (if + // not null), to be used to annotate a UMA report with a particular + // configuration state. Returns true on success. + // See the comment on MetricsService::RegisterSyntheticMultiGroupFieldTrial() + // for details. + static bool RegisterSyntheticMultiGroupFieldTrial( + MetricsService* metrics_service, + const std::string& trial_name, + const std::vector<uint32_t>& group_name_hashes); + // Same as RegisterSyntheticFieldTrial above, but takes in the trial name as a // hash rather than computing the hash from the string. static bool RegisterSyntheticFieldTrialWithNameHash( diff --git a/chromium/components/metrics/metrics_service_client.h b/chromium/components/metrics/metrics_service_client.h index 8624477d567..af4257784f5 100644 --- a/chromium/components/metrics/metrics_service_client.h +++ b/chromium/components/metrics/metrics_service_client.h @@ -41,13 +41,6 @@ class MetricsServiceClient { // when metrics recording gets enabled. virtual void SetMetricsClientId(const std::string& client_id) = 0; - // Notifies the client that recording is disabled, so that other services - // (such as crash reporting) can clear any association with metrics. - virtual void OnRecordingDisabled() = 0; - - // Whether there's an "off the record" (aka "Incognito") session active. - virtual bool IsOffTheRecordSessionActive() = 0; - // Returns the product value to use in uploaded reports, which will be used to // set the ChromeUserMetricsExtension.product field. See comments on that // field on why it's an int32_t rather than an enum. diff --git a/chromium/components/metrics/metrics_service_unittest.cc b/chromium/components/metrics/metrics_service_unittest.cc index 96e62f141c0..37dbbddbdbd 100644 --- a/chromium/components/metrics/metrics_service_unittest.cc +++ b/chromium/components/metrics/metrics_service_unittest.cc @@ -393,6 +393,53 @@ TEST_F(MetricsServiceTest, RegisterSyntheticTrial) { service.log_manager_.FinishCurrentLog(); } +TEST_F(MetricsServiceTest, RegisterSyntheticMultiGroupFieldTrial) { + TestMetricsServiceClient client; + MetricsService service(GetMetricsStateManager(), &client, GetLocalState()); + + // Register a synthetic trial TestTrial1 with groups A and B. + uint32_t trial_name_hash = HashName("TestTrial1"); + std::vector<uint32_t> group_name_hashes = {HashName("A"), HashName("B")}; + service.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash, + group_name_hashes); + // Ensure that time has advanced by at least a tick before proceeding. + WaitUntilTimeChanges(base::TimeTicks::Now()); + + service.log_manager_.BeginLoggingWithLog(std::unique_ptr<MetricsLog>( + new MetricsLog("clientID", 1, MetricsLog::INITIAL_STABILITY_LOG, &client, + GetLocalState()))); + + std::vector<variations::ActiveGroupId> synthetic_trials; + service.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(), + &synthetic_trials); + EXPECT_EQ(2U, synthetic_trials.size()); + EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "A")); + EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "B")); + + // Change the group for the trial to a single group. + group_name_hashes = {HashName("X")}; + service.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash, + group_name_hashes); + // Ensure that time has advanced by at least a tick before proceeding. + WaitUntilTimeChanges(base::TimeTicks::Now()); + + service.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(), + &synthetic_trials); + EXPECT_EQ(1U, synthetic_trials.size()); + EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "X")); + + // Register a trial with no groups, which should effectively remove the trial. + group_name_hashes.clear(); + service.RegisterSyntheticMultiGroupFieldTrial(trial_name_hash, + group_name_hashes); + // Ensure that time has advanced by at least a tick before proceeding. + WaitUntilTimeChanges(base::TimeTicks::Now()); + + service.GetSyntheticFieldTrialsOlderThan(base::TimeTicks::Now(), + &synthetic_trials); + service.log_manager_.FinishCurrentLog(); +} + TEST_F(MetricsServiceTest, MetricsProviderOnRecordingDisabledCalledOnInitialStop) { TestMetricsServiceClient client; diff --git a/chromium/components/metrics/metrics_state_manager.cc b/chromium/components/metrics/metrics_state_manager.cc index cf1b4a126ab..4cae20d3ae7 100644 --- a/chromium/components/metrics/metrics_state_manager.cc +++ b/chromium/components/metrics/metrics_state_manager.cc @@ -83,10 +83,15 @@ bool MetricsStateManager::IsMetricsReportingEnabled() { } void MetricsStateManager::ForceClientIdCreation() { - if (!client_id_.empty()) - return; + { + std::string client_id_from_prefs = + local_state_->GetString(prefs::kMetricsClientID); + // If client id in prefs matches the cached copy, return early. + if (!client_id_from_prefs.empty() && client_id_from_prefs == client_id_) + return; + client_id_.swap(client_id_from_prefs); + } - client_id_ = local_state_->GetString(prefs::kMetricsClientID); if (!client_id_.empty()) { // It is technically sufficient to only save a backup of the client id when // it is initially generated below, but since the backup was only introduced @@ -256,8 +261,10 @@ MetricsStateManager::LoadClientInfoAndMaybeMigrate() { } // The GUID retrieved (and possibly fixed above) should be valid unless - // retrieval failed. - DCHECK(!client_info || base::IsValidGUID(client_info->client_id)); + // retrieval failed. If not, return nullptr. This will result in a new GUID + // being generated by the calling function ForceClientIdCreation(). + if (client_info && !base::IsValidGUID(client_info->client_id)) + return nullptr; return client_info; } diff --git a/chromium/components/metrics/net/cellular_logic_helper.cc b/chromium/components/metrics/net/cellular_logic_helper.cc index 3651e878cdc..33b351e0a4d 100644 --- a/chromium/components/metrics/net/cellular_logic_helper.cc +++ b/chromium/components/metrics/net/cellular_logic_helper.cc @@ -4,7 +4,6 @@ #include "components/metrics/net/cellular_logic_helper.h" -#include "components/variations/variations_associated_data.h" #include "net/base/network_change_notifier.h" namespace metrics { @@ -21,10 +20,8 @@ const int kStandardUploadIntervalSeconds = 30 * 60; // Thirty minutes. #if defined(OS_ANDROID) const bool kDefaultCellularLogicEnabled = true; -const bool kDefaultCellularLogicOptimization = true; #else const bool kDefaultCellularLogicEnabled = false; -const bool kDefaultCellularLogicOptimization = false; #endif } // namespace @@ -37,22 +34,10 @@ base::TimeDelta GetUploadInterval() { return base::TimeDelta::FromSeconds(kStandardUploadIntervalSeconds); } -// Returns true if current connection type is cellular and user is assigned to -// experimental group for enabled cellular uploads. +// Returns true if current connection type is cellular and cellular logic is +// enabled. bool IsCellularLogicEnabled() { - std::string enabled = variations::GetVariationParamValue( - "UMA_EnableCellularLogUpload", "Enabled"); - std::string optimized = variations::GetVariationParamValue( - "UMA_EnableCellularLogUpload", "Optimize"); - bool is_enabled = kDefaultCellularLogicEnabled; - if (!enabled.empty()) - is_enabled = (enabled == "true"); - - bool is_optimized = kDefaultCellularLogicOptimization; - if (!optimized.empty()) - is_optimized = (optimized == "true"); - - if (!is_enabled || !is_optimized) + if (!kDefaultCellularLogicEnabled) return false; return net::NetworkChangeNotifier::IsConnectionCellular( diff --git a/chromium/components/metrics/persisted_logs.cc b/chromium/components/metrics/persisted_logs.cc index 52292da55cc..12d3a682188 100644 --- a/chromium/components/metrics/persisted_logs.cc +++ b/chromium/components/metrics/persisted_logs.cc @@ -4,12 +4,15 @@ #include "components/metrics/persisted_logs.h" +#include <memory> #include <string> +#include <utility> #include "base/base64.h" #include "base/md5.h" #include "base/metrics/histogram_macros.h" #include "base/sha1.h" +#include "base/strings/string_number_conversions.h" #include "base/timer/elapsed_timer.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" @@ -19,6 +22,10 @@ namespace metrics { namespace { +const char kLogHashKey[] = "hash"; +const char kLogTimestampKey[] = "timestamp"; +const char kLogDataKey[] = "data"; + PersistedLogs::LogReadStatus MakeRecallStatusHistogram( PersistedLogs::LogReadStatus status) { UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecallProtobufs", @@ -37,16 +44,22 @@ bool ReadBase64String(const base::ListValue& list_value, return base::Base64Decode(base64_result, result); } -// Base64-encodes |str| and appends the result to |list_value|. -void AppendBase64String(const std::string& str, base::ListValue* list_value) { - std::string base64_str; - base::Base64Encode(str, &base64_str); - list_value->AppendString(base64_str); +std::string EncodeToBase64(const std::string& to_convert) { + std::string base64_result; + base::Base64Encode(to_convert, &base64_result); + return base64_result; +} + +std::string DecodeFromBase64(const std::string& to_convert) { + std::string result; + base::Base64Decode(to_convert, &result); + return result; } } // namespace -void PersistedLogs::LogHashPair::Init(const std::string& log_data) { +void PersistedLogs::LogInfo::Init(const std::string& log_data, + const std::string& log_timestamp) { DCHECK(!log_data.empty()); if (!compression::GzipCompress(log_data, &compressed_log_data)) { @@ -59,15 +72,18 @@ void PersistedLogs::LogHashPair::Init(const std::string& log_data) { static_cast<int>(100 * compressed_log_data.size() / log_data.size())); hash = base::SHA1HashString(log_data); + timestamp = log_timestamp; } PersistedLogs::PersistedLogs(PrefService* local_state, const char* pref_name, + const char* outdated_pref_name, size_t min_log_count, size_t min_log_bytes, size_t max_log_size) : local_state_(local_state), pref_name_(pref_name), + outdated_pref_name_(outdated_pref_name), min_log_count_(min_log_count), min_log_bytes_(min_log_bytes), max_log_size_(max_log_size != 0 ? max_log_size : static_cast<size_t>(-1)), @@ -82,15 +98,26 @@ PersistedLogs::~PersistedLogs() {} void PersistedLogs::SerializeLogs() const { ListPrefUpdate update(local_state_, pref_name_); WriteLogsToPrefList(update.Get()); + + // After writing all the logs to the new pref remove old outdated pref. + // TODO(gayane): Remove when all users are migrated. crbug.com/649440 + if (local_state_->HasPrefPath(outdated_pref_name_)) + local_state_->ClearPref(outdated_pref_name_); } PersistedLogs::LogReadStatus PersistedLogs::DeserializeLogs() { + // TODO(gayane): Remove the code for reading logs from outdated pref when all + // users are migrated. crbug.com/649440 + if (local_state_->HasPrefPath(outdated_pref_name_)) { + return ReadLogsFromOldFormatPrefList( + *local_state_->GetList(outdated_pref_name_)); + } return ReadLogsFromPrefList(*local_state_->GetList(pref_name_)); } void PersistedLogs::StoreLog(const std::string& log_data) { - list_.push_back(LogHashPair()); - list_.back().Init(log_data); + list_.push_back(LogInfo()); + list_.back().Init(log_data, base::Int64ToString(base::Time::Now().ToTimeT())); } void PersistedLogs::StageLog() { @@ -109,6 +136,38 @@ void PersistedLogs::DiscardStagedLog() { staged_log_index_ = -1; } +PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromPrefList( + const base::ListValue& list_value) { + if (list_value.empty()) + return MakeRecallStatusHistogram(LIST_EMPTY); + + const size_t log_count = list_value.GetSize(); + + DCHECK(list_.empty()); + list_.resize(log_count); + + for (size_t i = 0; i < log_count; ++i) { + const base::DictionaryValue* dict; + if (!list_value.GetDictionary(i, &dict) || + !dict->GetString(kLogDataKey, &list_[i].compressed_log_data) || + !dict->GetString(kLogHashKey, &list_[i].hash)) { + list_.clear(); + return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION); + } + + list_[i].compressed_log_data = + DecodeFromBase64(list_[i].compressed_log_data); + list_[i].hash = DecodeFromBase64(list_[i].hash); + // Ignoring the success of this step as timestamp might not be there for + // older logs. + // NOTE: Should be added to the check with other fields once migration is + // over. + dict->GetString(kLogTimestampKey, &list_[i].timestamp); + } + + return MakeRecallStatusHistogram(RECALL_SUCCESS); +} + void PersistedLogs::WriteLogsToPrefList(base::ListValue* list_value) const { list_value->Clear(); @@ -140,14 +199,19 @@ void PersistedLogs::WriteLogsToPrefList(base::ListValue* list_value) const { dropped_logs_num++; continue; } - AppendBase64String(list_[i].compressed_log_data, list_value); - AppendBase64String(list_[i].hash, list_value); + std::unique_ptr<base::DictionaryValue> dict_value( + new base::DictionaryValue); + dict_value->SetString(kLogHashKey, EncodeToBase64(list_[i].hash)); + dict_value->SetString(kLogDataKey, + EncodeToBase64(list_[i].compressed_log_data)); + dict_value->SetString(kLogTimestampKey, list_[i].timestamp); + list_value->Append(std::move(dict_value)); } if (dropped_logs_num > 0) UMA_HISTOGRAM_COUNTS("UMA.UnsentLogs.Dropped", dropped_logs_num); } -PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromPrefList( +PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromOldFormatPrefList( const base::ListValue& list_value) { if (list_value.empty()) return MakeRecallStatusHistogram(LIST_EMPTY); diff --git a/chromium/components/metrics/persisted_logs.h b/chromium/components/metrics/persisted_logs.h index 73ad79a5b06..479924fd0fd 100644 --- a/chromium/components/metrics/persisted_logs.h +++ b/chromium/components/metrics/persisted_logs.h @@ -51,6 +51,7 @@ class PersistedLogs { // that limit will be skipped when writing to disk. PersistedLogs(PrefService* local_state, const char* pref_name, + const char* outdated_pref_name, size_t min_log_count, size_t min_log_bytes, size_t max_log_size); @@ -87,6 +88,12 @@ class PersistedLogs { return list_[staged_log_index_].hash; } + // Returns the timestamp of the element in the front of the list. + const std::string& staged_log_timestamp() const { + DCHECK(has_staged_log()); + return list_[staged_log_index_].timestamp; + } + // The number of elements currently stored. size_t size() const { return list_.size(); } @@ -100,6 +107,9 @@ class PersistedLogs { // Reads the list from the ListValue. LogReadStatus ReadLogsFromPrefList(const base::ListValue& list); + // Reads the list from the ListValue in the old Log-hash pair format. + LogReadStatus ReadLogsFromOldFormatPrefList(const base::ListValue& list); + // A weak pointer to the PrefService object to read and write the preference // from. Calling code should ensure this object continues to exist for the // lifetime of the PersistedLogs object. @@ -108,6 +118,10 @@ class PersistedLogs { // The name of the preference to serialize logs to/from. const char* pref_name_; + // The name of the preference to serialize logs to/from which may contain log + // in the old formatting. + const char* outdated_pref_name_; + // We will keep at least this |min_log_count_| logs or |min_log_bytes_| bytes // of logs, whichever is greater, when writing to disk. These apply after // skipping logs greater than |max_log_size_|. @@ -117,19 +131,23 @@ class PersistedLogs { // Logs greater than this size will not be written to disk. const size_t max_log_size_; - struct LogHashPair { - // Initializes the members based on uncompressed |log_data|. - void Init(const std::string& log_data); + struct LogInfo { + // Initializes the members based on uncompressed |log_data| and + // |log_timestamp|. + void Init(const std::string& log_data, const std::string& log_timestamp); // Compressed log data - a serialized protobuf that's been gzipped. std::string compressed_log_data; // The SHA1 hash of log, stored to catch errors from memory corruption. std::string hash; + + // The timestamp of when the log was created as a time_t value. + std::string timestamp; }; // A list of all of the stored logs, stored with SHA1 hashes to check for // corruption while they are stored in memory. - std::vector<LogHashPair> list_; + std::vector<LogInfo> list_; // The index and type of the log staged for upload. If nothing has been // staged, the index will be -1. diff --git a/chromium/components/metrics/persisted_logs_unittest.cc b/chromium/components/metrics/persisted_logs_unittest.cc index b4a79a73ea6..94b8b7e97de 100644 --- a/chromium/components/metrics/persisted_logs_unittest.cc +++ b/chromium/components/metrics/persisted_logs_unittest.cc @@ -22,6 +22,7 @@ namespace metrics { namespace { const char kTestPrefName[] = "TestPref"; +const char kTestOutdatedPrefName[] = "OutdatedTestPref"; const size_t kLogCountLimit = 3; const size_t kLogByteLimit = 1000; @@ -63,9 +64,12 @@ class PersistedLogsTest : public testing::Test { class TestPersistedLogs : public PersistedLogs { public: TestPersistedLogs(PrefService* service, size_t min_log_bytes) - : PersistedLogs(service, kTestPrefName, kLogCountLimit, min_log_bytes, - 0) { - } + : PersistedLogs(service, + kTestPrefName, + kTestOutdatedPrefName, + kLogCountLimit, + min_log_bytes, + 0) {} // Stages and removes the next log, while testing it's value. void ExpectNextLog(const std::string& expected_log) { @@ -111,6 +115,8 @@ TEST_F(PersistedLogsTest, SingleElementLogList) { EXPECT_EQ(persisted_logs.staged_log(), result_persisted_logs.staged_log()); EXPECT_EQ(persisted_logs.staged_log_hash(), result_persisted_logs.staged_log_hash()); + EXPECT_EQ(persisted_logs.staged_log_timestamp(), + result_persisted_logs.staged_log_timestamp()); } // Store a set of logs over the length limit, but smaller than the min number of diff --git a/chromium/components/metrics/profiler/profiler_metrics_provider.cc b/chromium/components/metrics/profiler/profiler_metrics_provider.cc index df64e293aee..2ab8c0314f6 100644 --- a/chromium/components/metrics/profiler/profiler_metrics_provider.cc +++ b/chromium/components/metrics/profiler/profiler_metrics_provider.cc @@ -107,7 +107,8 @@ void ProfilerMetricsProvider::RecordProfilerData( if (IsCellularLogicEnabled()) return; - const bool new_phase = !ContainsKey(profiler_events_cache_, profiling_phase); + const bool new_phase = + !base::ContainsKey(profiler_events_cache_, profiling_phase); ProfilerEventProto* profiler_event = &profiler_events_cache_[profiling_phase]; if (new_phase) { diff --git a/chromium/components/metrics/profiler/tracking_synchronizer.cc b/chromium/components/metrics/profiler/tracking_synchronizer.cc index 21ccb85f532..1dc8fa75db4 100644 --- a/chromium/components/metrics/profiler/tracking_synchronizer.cc +++ b/chromium/components/metrics/profiler/tracking_synchronizer.cc @@ -9,7 +9,7 @@ #include "base/bind.h" #include "base/callback.h" -#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros.h" #include "base/threading/thread.h" #include "base/threading/thread_task_runner_handle.h" #include "base/tracked_objects.h" diff --git a/chromium/components/metrics/profiler/tracking_synchronizer_unittest.cc b/chromium/components/metrics/profiler/tracking_synchronizer_unittest.cc index 016315caf46..cae6cb208fe 100644 --- a/chromium/components/metrics/profiler/tracking_synchronizer_unittest.cc +++ b/chromium/components/metrics/profiler/tracking_synchronizer_unittest.cc @@ -120,8 +120,8 @@ class TestTrackingSynchronizer : public TrackingSynchronizer { TEST(TrackingSynchronizerTest, ProfilerData) { // Testing how TrackingSynchronizer reports 2 phases of profiling. - auto clock = new base::SimpleTestTickClock(); // Will be owned by - // |tracking_synchronizer|. + auto* clock = new base::SimpleTestTickClock(); // Will be owned by + // |tracking_synchronizer|. clock->Advance(base::TimeDelta::FromMilliseconds(111)); scoped_refptr<TestTrackingSynchronizer> tracking_synchronizer = diff --git a/chromium/components/metrics/proto/BUILD.gn b/chromium/components/metrics/proto/BUILD.gn index 3271f184cdf..0afdeea21b7 100644 --- a/chromium/components/metrics/proto/BUILD.gn +++ b/chromium/components/metrics/proto/BUILD.gn @@ -4,12 +4,12 @@ import("//third_party/protobuf/proto_library.gni") -# GYP version: components/ proto_library("proto") { sources = [ "call_stack_profile.proto", "cast_logs.proto", "chrome_user_metrics_extension.proto", + "execution_context.proto", "histogram_event.proto", "memory_leak_report.proto", "omnibox_event.proto", diff --git a/chromium/components/metrics/proto/cast_logs.proto b/chromium/components/metrics/proto/cast_logs.proto index 951dfb24767..987c4f5dde6 100644 --- a/chromium/components/metrics/proto/cast_logs.proto +++ b/chromium/components/metrics/proto/cast_logs.proto @@ -22,6 +22,7 @@ message CastLogsProto { enum CastProductType { CAST_PRODUCT_TYPE_UNKNOWN = 0; CAST_PRODUCT_TYPE_CHROMECAST = 1; + CAST_PRODUCT_TYPE_TV = 2; CAST_PRODUCT_TYPE_AUDIO = 3; CAST_PRODUCT_TYPE_ANDROID_TV = 4; } @@ -50,7 +51,7 @@ message CastLogsProto { // This message describes a detail sender device and sdk. Those are // parsed from the user agent string sent from sender sdk during connection. - // Next tag: 9 + // Next tag: 10 message SenderInfo { // The identifier for the sender device, that is not tied any kind of // device id outside of UMA, and this id is reset when user resets sender @@ -107,6 +108,11 @@ message CastLogsProto { // Sender device model. optional string model = 8; + + // Last 2 bytes of the sender’s local IP addresses (both IP4/IP6) when + // the sender connected. This field stores ip fragment to last 2 bytes and + // first 2 bytes won't be used. + optional int32 sender_local_ip_fragment = 9; } optional SenderInfo sender_info = 3; } @@ -115,7 +121,7 @@ message CastLogsProto { repeated CastConnectionInfo cast_connection_info = 2; // Stores Cast-enabled device specific events with a various context data. - // Next tag: 10 + // Next tag: 12 message CastEventProto { // The name of the action, hashed by same logic used to hash user action // event and histogram. @@ -147,6 +153,10 @@ message CastLogsProto { // An optional value for the multi-room group uuid. optional fixed64 group_uuid = 10; + + // For application events associated with an assistant session, identifies + // the assistant conversation. + optional string conversation_key = 11; } repeated CastEventProto cast_event = 3; @@ -173,6 +183,10 @@ message CastLogsProto { // System version which the cast_shell is running. optional fixed64 system_build_number = 2; + + // An identifier that is specific to the combination of app and device, in + // this case the one used by backdrop. + optional string backdrop_app_device_id = 3; } optional CastDeviceMutableInfo cast_device_mutable_info = 5; diff --git a/chromium/components/metrics/proto/chrome_user_metrics_extension.proto b/chromium/components/metrics/proto/chrome_user_metrics_extension.proto index 8b82933ab77..4b9bf68be16 100644 --- a/chromium/components/metrics/proto/chrome_user_metrics_extension.proto +++ b/chromium/components/metrics/proto/chrome_user_metrics_extension.proto @@ -31,6 +31,9 @@ message ChromeUserMetricsExtension { // UMA metrics from Android Webview. ANDROID_WEBVIEW = 20; + + // Cast receivers, e.g. Chromecast + CAST = 35; } // The product corresponding to this log. The field type is int32 instead of // Product so that downstream users of the Chromium metrics component can diff --git a/chromium/components/metrics/proto/execution_context.proto b/chromium/components/metrics/proto/execution_context.proto new file mode 100644 index 00000000000..3d8679fe9db --- /dev/null +++ b/chromium/components/metrics/proto/execution_context.proto @@ -0,0 +1,49 @@ +// Copyright 2016 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. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; +option java_outer_classname = "ExecutionContextProtos"; +option java_package = "org.chromium.components.metrics"; + +package metrics; + +// Enums corresponding to the Chrome execution context in which data was +// collected. + +// Chrome process type. Derived from content/public/common/process_type.h. +enum Process { + UNKNOWN_PROCESS = 0; + BROWSER_PROCESS = 1; + RENDERER_PROCESS = 2; + GPU_PROCESS = 3; + UTILITY_PROCESS = 4; + ZYGOTE_PROCESS = 5; + SANDBOX_HELPER_PROCESS = 6; + PPAPI_PLUGIN_PROCESS = 7; + PPAPI_BROKER_PROCESS = 8; +} + +// Chrome thread. This list is not exhaustive. +enum Thread { + UNKNOWN_THREAD = 0; + + // Browser process threads from content/public/browser/browser_thread.h, + // some of which occur in other processes as well. + UI_THREAD = 1; + FILE_THREAD = 2; + FILE_USER_BLOCKING_THREAD = 3; + PROCESS_LAUNCHER_THREAD = 4; + CACHE_THREAD = 5; + IO_THREAD = 6; + DB_THREAD = 7; + + // GPU process thread. + GPU_MAIN_THREAD = 8; + + // Renderer process threads. + RENDER_THREAD = 9; + UTILITY_THREAD = 10; +} diff --git a/chromium/components/metrics/proto/memory_leak_report.proto b/chromium/components/metrics/proto/memory_leak_report.proto index bbd2dbc7a66..1a998fefa0b 100644 --- a/chromium/components/metrics/proto/memory_leak_report.proto +++ b/chromium/components/metrics/proto/memory_leak_report.proto @@ -8,7 +8,7 @@ option optimize_for = LITE_RUNTIME; package metrics; -// Next tag: 6 +// Next tag: 10 message MemoryLeakReportProto { // The call stack at which the leak was found. This is a list of offsets // within the program binary. The first entry is the deepest level of the call @@ -69,6 +69,12 @@ message MemoryLeakReportProto { } optional ProcessType source_process = 5; + // The build ID of the Chrome binary from which this leak report was obtained. + // The build ID is typically a 16- or 20-byte hash that is generated by the + // compiler that built the binary. This value will be read directly from the + // GNU build notes section of the Chrome binary. + optional bytes build_id = 6; + ////////////////////////////////////////////////////////////////////////////// // Represents a single snapshot of the internal bookkeeping of the Runtime @@ -96,4 +102,39 @@ message MemoryLeakReportProto { // allocation. The oldest record is at the beginning. The most recent record, // taken at the time the report was generated, is at the end. repeated AllocationBreakdown alloc_breakdown_history = 4; + + // The following two fields describe the last increasing trend in the number + // of allocations from the size and call stack that generated this + // leak report. + // + // |num_rising_intervals| equals timeslot_now - timeslot_drop, + // where timeslot_drop is the timeslot number of the last frame that saw + // a drop in the number of allocations (or 0 if there were no drops). + // If it is < 32, it will be visible in the allocation history graph. + // If it is >= 32, it will not be seen in the graph. + // E.g. for history [3,2,4,4,7] |num_rising_intervals| equals 3. + optional uint32 num_rising_intervals = 7; + + // Indicates the magnitude of the current uptrend in allocations. + // E.g. for history [3,2,4,4,7] |num_allocs_increase| equals 5. + optional uint32 num_allocs_increase = 8; + + ////////////////////////////////////////////////////////////////////////////// + + // Contains additional data about the memory usage from the OS. + // There is no need to store the total system memory as it is + // available under SystemProfileProto::Hardware::system_ram_mb. + // + // Next tag: 3 + message MemoryUsageInfo { + // How much available physical memory the system has. + optional uint64 available_ram_mb = 1; + + // Total private working set memory across all Chrome processes. + optional uint64 chrome_ram_usage_mb = 2; + } + + // Information about the memory usage from the OS collected right after + // the leak report was created in the leak detector. + optional MemoryUsageInfo memory_usage_info = 9; } diff --git a/chromium/components/metrics/proto/omnibox_event.proto b/chromium/components/metrics/proto/omnibox_event.proto index 5d4f7199657..2f8f24da94f 100644 --- a/chromium/components/metrics/proto/omnibox_event.proto +++ b/chromium/components/metrics/proto/omnibox_event.proto @@ -111,10 +111,8 @@ message OmniboxEventProto { // The instant new tab page enum value was deprecated on August 2, 2013. OBSOLETE_INSTANT_NTP = 5; - // The user is on a search result page that's doing search term - // replacement, meaning the search terms should've appeared in the omnibox - // before the user started editing it, not the URL of the page. - SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT = 6; + // The search term replacement enum value was deprecated in August 2016. + OBSOLETE_SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT = 6; // The new tab page in which this omnibox interaction first started // with the user having focus in the omnibox. @@ -168,6 +166,8 @@ message OmniboxEventProto { // a suggestion powered by a Chrome content provider. ON_DEVICE_CHROME = 13; CLIPBOARD_URL = 14; // Suggestion coming from clipboard (iOS only). + PHYSICAL_WEB = 15; // Suggestions triggered by URLs broadcast by nearby + // devices (iOS only). } // The result set displayed on the completion popup @@ -226,6 +226,9 @@ message OmniboxEventProto { // suggestions of any type. CALCULATOR = 23; // A calculator answer. CLIPBOARD = 24; // An URL based on the clipboard. + PHYSICAL_WEB = 25; // A Physical Web nearby URL. + PHYSICAL_WEB_OVERFLOW = 26; // An item representing multiple Physical + // Web nearby URLs. } optional ResultType result_type = 2; diff --git a/chromium/components/metrics/proto/sampled_profile.proto b/chromium/components/metrics/proto/sampled_profile.proto index b8936e114c6..0d8fcae42b5 100644 --- a/chromium/components/metrics/proto/sampled_profile.proto +++ b/chromium/components/metrics/proto/sampled_profile.proto @@ -11,13 +11,14 @@ option java_package = "org.chromium.components.metrics"; package metrics; import "call_stack_profile.proto"; +import "execution_context.proto"; import "perf_data.proto"; import "perf_stat.proto"; // Protocol buffer for collected sample-based profiling data. // Contains the parameters and data from a single profile collection event. -// Next tag: 11 +// Next tag: 13 message SampledProfile { // Indicates the event that triggered this collection. enum TriggerEvent { @@ -35,7 +36,7 @@ message SampledProfile { // The profile was collected upon restoring a previous session. RESTORE_SESSION = 3; - + // The profile was collected at process startup. PROCESS_STARTUP = 4; @@ -47,6 +48,12 @@ message SampledProfile { } optional TriggerEvent trigger_event = 1; + // The process in which the profile was collected. + optional Process process = 11; + + // The thread in which the profile was collected. + optional Thread thread = 12; + // Fields 2-3: Time durations are given in ticks, and represent system uptime // rather than wall time. diff --git a/chromium/components/metrics/proto/system_profile.proto b/chromium/components/metrics/proto/system_profile.proto index d814b3597fe..c116a0a1d19 100644 --- a/chromium/components/metrics/proto/system_profile.proto +++ b/chromium/components/metrics/proto/system_profile.proto @@ -440,7 +440,7 @@ message SystemProfileProto { // Figures that can be used to generate application stability metrics. // All values are counts of events since the last time that these // values were reported. - // Next tag: 26 + // Next tag: 28 message Stability { // Total amount of time that the program was running, in seconds, // since the last time a log was recorded, as measured using a client-side @@ -458,17 +458,19 @@ message SystemProfileProto { // This field was added for M-35. optional int64 uptime_sec = 23; - // Page loads along with renderer crashes, hangs and failed launches, since - // page load count roughly corresponds to usage. + // Page loads along with renderer launches, crashes, hangs and failed + // launches, since page load count roughly corresponds to usage. optional int32 page_load_count = 2; optional int32 renderer_crash_count = 3; optional int32 renderer_hang_count = 4; optional int32 renderer_failed_launch_count = 24; + optional int32 renderer_launch_count = 26; - // Number of renderer crashes and failed launches that were for extensions. - // These are not counted in the renderer counts above. + // Number of renderer launches, crashes and failed launches that were for + // extensions. These are not counted in the renderer counts above. optional int32 extension_renderer_crash_count = 5; optional int32 extension_renderer_failed_launch_count = 25; + optional int32 extension_renderer_launch_count = 27; // Number of non-renderer child process crashes. optional int32 child_process_crash_count = 6; @@ -602,10 +604,10 @@ message SystemProfileProto { // be consistent between manufacturers. optional int32 manufacture_week = 6; - // Max horizontal resolution in pixels. + // Selected horizontal resolution in pixels. optional int32 horizontal_resolution = 7; - // Max vertical resolution in pixels. + // Selected vertical resolution in pixels. optional int32 vertical_resolution = 8; // Audio capabilities of the device. @@ -651,7 +653,6 @@ message SystemProfileProto { DIGITAL = 1; } optional OutputMode output_mode = 6; - } repeated AudioDescription audio_description = 9; @@ -704,6 +705,50 @@ message SystemProfileProto { optional int32 num_aborted_unrecognized = 7; } repeated CECCommand cec_command = 13; + + // Selected Frame rate + optional int32 frame_rate = 14; + + // Selected color encoding. + enum ColorEncoding { + COLOR_ENCODING_UNKNOWN = 0; + COLOR_ENCODING_RGB = 1; + COLOR_ENCODING_YUV444 = 2; + COLOR_ENCODING_YUV422 = 3; + COLOR_ENCODING_YUV420 = 4; + } + optional ColorEncoding color_encoding = 15; + + // Selected bit-depth. + optional int32 bit_depth = 16; + + // Devices's max TMDS char rate. + optional int32 tmds = 17; + + // HDR10 support. + optional bool hdr10_support = 18; + + // Dolby vision support. + optional bool dolby_vision_support = 19; + + // Supported EOTFs. + // EOTF support according to the spec: + // eotf_support & 0x1 -> SDR supported + // (eotf_support > 1) & 0x1 -> traditional HDR supported + // (eotf_support > 2) & 0x1 -> ST2084 supported + optional int32 eotf_support = 20; + + // Supports YUV. + optional bool yuv_support = 21; + + // Supports YUV_420. + optional bool yuv_420_support = 22; + + // The maximum HDCP version supported by the sink. + optional int32 maximum_supported_hdcp_version = 23; + + // The current HDCP version negotiated with the sink. + optional int32 current_hdcp_version = 24; } repeated ExternalAudioVideoDevice external_audio_video_device = 14; diff --git a/chromium/components/metrics/public/cpp/BUILD.gn b/chromium/components/metrics/public/cpp/BUILD.gn new file mode 100644 index 00000000000..ca2f05741c0 --- /dev/null +++ b/chromium/components/metrics/public/cpp/BUILD.gn @@ -0,0 +1,19 @@ +# Copyright 2016 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. + +import("//mojo/public/tools/bindings/mojom.gni") + +source_set("call_stack_unit_tests") { + testonly = true + sources = [ + "call_stack_profile_struct_traits_unittest.cc", + ] + + deps = [ + "//base", + "//components/metrics/public/interfaces:call_stack_mojo_test_bindings", + "//mojo/public/cpp/bindings", + "//testing/gtest", + ] +} diff --git a/chromium/components/metrics/public/cpp/OWNERS b/chromium/components/metrics/public/cpp/OWNERS new file mode 100644 index 00000000000..154435234ea --- /dev/null +++ b/chromium/components/metrics/public/cpp/OWNERS @@ -0,0 +1,4 @@ +per-file *.mojom=set noparent +per-file *.mojom=file://ipc/SECURITY_OWNERS +per-file *_struct_traits*.*=set noparent +per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS diff --git a/chromium/components/metrics/public/cpp/call_stack_profile.typemap b/chromium/components/metrics/public/cpp/call_stack_profile.typemap new file mode 100644 index 00000000000..a12ba83f473 --- /dev/null +++ b/chromium/components/metrics/public/cpp/call_stack_profile.typemap @@ -0,0 +1,26 @@ +# Copyright 2016 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. + +mojom = + "//components/metrics/public/interfaces/call_stack_profile_collector.mojom" +public_headers = [ + "//base/profiler/stack_sampling_profiler.h", + "//components/metrics/call_stack_profile_params.h", +] +traits_headers = + [ "//components/metrics/public/cpp/call_stack_profile_struct_traits.h" ] +deps = [ + "//base", + "//components/metrics:call_stack_profile_params", +] +type_mappings = [ + "metrics.mojom.CallStackModule=base::StackSamplingProfiler::Module", + "metrics.mojom.CallStackFrame=base::StackSamplingProfiler::Frame", + "metrics.mojom.CallStackProfile=base::StackSamplingProfiler::CallStackProfile", + "metrics.mojom.CallStackProfileParams=metrics::CallStackProfileParams", + "metrics.mojom.Process=metrics::CallStackProfileParams::Process", + "metrics.mojom.SampleOrderingSpec=metrics::CallStackProfileParams::SampleOrderingSpec", + "metrics.mojom.Thread=metrics::CallStackProfileParams::Thread", + "metrics.mojom.Trigger=metrics::CallStackProfileParams::Trigger", +] diff --git a/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits.h b/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits.h new file mode 100644 index 00000000000..6e200341c09 --- /dev/null +++ b/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits.h @@ -0,0 +1,384 @@ +// Copyright 2016 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. + +// Defines StructTraits specializations for translating between mojo types and +// base::StackSamplingProfiler types, with data validity checks. + +#ifndef COMPONENTS_METRICS_CALL_STACK_PROFILE_STRUCT_TRAITS_H_ +#define COMPONENTS_METRICS_CALL_STACK_PROFILE_STRUCT_TRAITS_H_ + +#include <vector> + +#include "base/files/file_path.h" +#include "base/profiler/stack_sampling_profiler.h" +#include "components/metrics/public/interfaces/call_stack_profile_collector.mojom.h" + +namespace mojo { + +template <> +struct StructTraits<metrics::mojom::CallStackModuleDataView, + base::StackSamplingProfiler::Module> { + static uint64_t base_address( + const base::StackSamplingProfiler::Module& module) { + return module.base_address; + } + static const std::string& id( + const base::StackSamplingProfiler::Module& module) { + return module.id; + } + static const base::FilePath& filename( + const base::StackSamplingProfiler::Module& module) { + return module.filename; + } + + static bool Read(metrics::mojom::CallStackModuleDataView data, + base::StackSamplingProfiler::Module* out) { + // Linux has the longest build id at 40 bytes. + static const size_t kMaxIDSize = 40; + + std::string id; + base::FilePath filename; + if (!data.ReadId(&id) || id.size() > kMaxIDSize || + !data.ReadFilename(&filename)) + return false; + + *out = + base::StackSamplingProfiler::Module(data.base_address(), id, filename); + return true; + } +}; + +template <> +struct StructTraits<metrics::mojom::CallStackFrameDataView, + base::StackSamplingProfiler::Frame> { + static uint64_t instruction_pointer( + const base::StackSamplingProfiler::Frame& frame) { + return frame.instruction_pointer; + } + static uint64_t module_index( + const base::StackSamplingProfiler::Frame& frame) { + return frame.module_index == + base::StackSamplingProfiler::Frame::kUnknownModuleIndex ? + static_cast<uint64_t>(-1) : + frame.module_index; + } + + static bool Read(metrics::mojom::CallStackFrameDataView data, + base::StackSamplingProfiler::Frame* out) { + size_t module_index = data.module_index() == static_cast<uint64_t>(-1) ? + base::StackSamplingProfiler::Frame::kUnknownModuleIndex : + data.module_index(); + + // We can't know whether the module_index field is valid at this point since + // we don't have access to the number of modules here. This will be checked + // in CallStackProfile's Read function below. + *out = base::StackSamplingProfiler::Frame(data.instruction_pointer(), + module_index); + return true; + } +}; + +template <> +struct StructTraits<metrics::mojom::CallStackProfileDataView, + base::StackSamplingProfiler::CallStackProfile> { + static const std::vector<base::StackSamplingProfiler::Module>& modules( + const base::StackSamplingProfiler::CallStackProfile& profile) { + return profile.modules; + } + static const std::vector<base::StackSamplingProfiler::Sample>& samples( + const base::StackSamplingProfiler::CallStackProfile& profile) { + return profile.samples; + } + static const base::TimeDelta profile_duration( + const base::StackSamplingProfiler::CallStackProfile& profile) { + return profile.profile_duration; + } + static const base::TimeDelta sampling_period( + const base::StackSamplingProfiler::CallStackProfile& profile) { + return profile.sampling_period; + } + + static bool ValidateSamples( + std::vector<base::StackSamplingProfiler::Sample> samples, + size_t module_count) { + for (const base::StackSamplingProfiler::Sample& sample : samples) { + for (const base::StackSamplingProfiler::Frame& frame : sample) { + if (frame.module_index >= module_count && + frame.module_index != + base::StackSamplingProfiler::Frame::kUnknownModuleIndex) + return false; + } + } + return true; + } + + static bool Read(metrics::mojom::CallStackProfileDataView data, + base::StackSamplingProfiler::CallStackProfile* out) { + std::vector<base::StackSamplingProfiler::Module> modules; + std::vector<base::StackSamplingProfiler::Sample> samples; + base::TimeDelta profile_duration, sampling_period; + if (!data.ReadModules(&modules) || !data.ReadSamples(&samples) || + !data.ReadProfileDuration(&profile_duration) || + !data.ReadSamplingPeriod(&sampling_period) || + !ValidateSamples(samples, modules.size())) + return false; + + *out = base::StackSamplingProfiler::CallStackProfile(); + out->modules = std::move(modules); + out->samples = std::move(samples); + out->profile_duration = profile_duration; + out->sampling_period = sampling_period; + return true; + } +}; + +template <> +struct EnumTraits<metrics::mojom::Process, + metrics::CallStackProfileParams::Process> { + static metrics::mojom::Process ToMojom( + metrics::CallStackProfileParams::Process process) { + switch (process) { + case metrics::CallStackProfileParams::Process::UNKNOWN_PROCESS: + return metrics::mojom::Process::UNKNOWN_PROCESS; + case metrics::CallStackProfileParams::Process::BROWSER_PROCESS: + return metrics::mojom::Process::BROWSER_PROCESS; + case metrics::CallStackProfileParams::Process::RENDERER_PROCESS: + return metrics::mojom::Process::RENDERER_PROCESS; + case metrics::CallStackProfileParams::Process::GPU_PROCESS: + return metrics::mojom::Process::GPU_PROCESS; + case metrics::CallStackProfileParams::Process::UTILITY_PROCESS: + return metrics::mojom::Process::UTILITY_PROCESS; + case metrics::CallStackProfileParams::Process::ZYGOTE_PROCESS: + return metrics::mojom::Process::ZYGOTE_PROCESS; + case metrics::CallStackProfileParams::Process::SANDBOX_HELPER_PROCESS: + return metrics::mojom::Process::SANDBOX_HELPER_PROCESS; + case metrics::CallStackProfileParams::Process::PPAPI_PLUGIN_PROCESS: + return metrics::mojom::Process::PPAPI_PLUGIN_PROCESS; + case metrics::CallStackProfileParams::Process::PPAPI_BROKER_PROCESS: + return metrics::mojom::Process::PPAPI_BROKER_PROCESS; + } + NOTREACHED(); + return metrics::mojom::Process::UNKNOWN_PROCESS; + } + + static bool FromMojom(metrics::mojom::Process process, + metrics::CallStackProfileParams::Process* out) { + switch (process) { + case metrics::mojom::Process::UNKNOWN_PROCESS: + *out = metrics::CallStackProfileParams::Process::UNKNOWN_PROCESS; + return true; + case metrics::mojom::Process::BROWSER_PROCESS: + *out = metrics::CallStackProfileParams::Process::BROWSER_PROCESS; + return true; + case metrics::mojom::Process::RENDERER_PROCESS: + *out = metrics::CallStackProfileParams::Process::RENDERER_PROCESS; + return true; + case metrics::mojom::Process::GPU_PROCESS: + *out = metrics::CallStackProfileParams::Process::GPU_PROCESS; + return true; + case metrics::mojom::Process::UTILITY_PROCESS: + *out = metrics::CallStackProfileParams::Process::UTILITY_PROCESS; + return true; + case metrics::mojom::Process::ZYGOTE_PROCESS: + *out = metrics::CallStackProfileParams::Process::ZYGOTE_PROCESS; + return true; + case metrics::mojom::Process::SANDBOX_HELPER_PROCESS: + *out = metrics::CallStackProfileParams::Process::SANDBOX_HELPER_PROCESS; + return true; + case metrics::mojom::Process::PPAPI_PLUGIN_PROCESS: + *out = metrics::CallStackProfileParams::Process::PPAPI_PLUGIN_PROCESS; + return true; + case metrics::mojom::Process::PPAPI_BROKER_PROCESS: + *out = metrics::CallStackProfileParams::Process::PPAPI_BROKER_PROCESS; + return true; + } + return false; + } +}; + +template <> +struct EnumTraits<metrics::mojom::Thread, + metrics::CallStackProfileParams::Thread> { + static metrics::mojom::Thread ToMojom( + metrics::CallStackProfileParams::Thread thread) { + switch (thread) { + case metrics::CallStackProfileParams::Thread::UNKNOWN_THREAD: + return metrics::mojom::Thread::UNKNOWN_THREAD; + case metrics::CallStackProfileParams::Thread::UI_THREAD: + return metrics::mojom::Thread::UI_THREAD; + case metrics::CallStackProfileParams::Thread::FILE_THREAD: + return metrics::mojom::Thread::FILE_THREAD; + case metrics::CallStackProfileParams::Thread::FILE_USER_BLOCKING_THREAD: + return metrics::mojom::Thread::FILE_USER_BLOCKING_THREAD; + case metrics::CallStackProfileParams::Thread::PROCESS_LAUNCHER_THREAD: + return metrics::mojom::Thread::PROCESS_LAUNCHER_THREAD; + case metrics::CallStackProfileParams::Thread::CACHE_THREAD: + return metrics::mojom::Thread::CACHE_THREAD; + case metrics::CallStackProfileParams::Thread::IO_THREAD: + return metrics::mojom::Thread::IO_THREAD; + case metrics::CallStackProfileParams::Thread::DB_THREAD: + return metrics::mojom::Thread::DB_THREAD; + case metrics::CallStackProfileParams::Thread::GPU_MAIN_THREAD: + return metrics::mojom::Thread::GPU_MAIN_THREAD; + case metrics::CallStackProfileParams::Thread::RENDER_THREAD: + return metrics::mojom::Thread::RENDER_THREAD; + case metrics::CallStackProfileParams::Thread::UTILITY_THREAD: + return metrics::mojom::Thread::UTILITY_THREAD; + } + NOTREACHED(); + return metrics::mojom::Thread::UNKNOWN_THREAD; + } + + static bool FromMojom(metrics::mojom::Thread thread, + metrics::CallStackProfileParams::Thread* out) { + switch (thread) { + case metrics::mojom::Thread::UNKNOWN_THREAD: + *out = metrics::CallStackProfileParams::Thread::UNKNOWN_THREAD; + return true; + case metrics::mojom::Thread::UI_THREAD: + *out = metrics::CallStackProfileParams::Thread::UI_THREAD; + return true; + case metrics::mojom::Thread::FILE_THREAD: + *out = metrics::CallStackProfileParams::Thread::FILE_THREAD; + return true; + case metrics::mojom::Thread::FILE_USER_BLOCKING_THREAD: + *out = + metrics::CallStackProfileParams::Thread::FILE_USER_BLOCKING_THREAD; + return true; + case metrics::mojom::Thread::PROCESS_LAUNCHER_THREAD: + *out = metrics::CallStackProfileParams::Thread::PROCESS_LAUNCHER_THREAD; + return true; + case metrics::mojom::Thread::CACHE_THREAD: + *out = metrics::CallStackProfileParams::Thread::CACHE_THREAD; + return true; + case metrics::mojom::Thread::IO_THREAD: + *out = metrics::CallStackProfileParams::Thread::IO_THREAD; + return true; + case metrics::mojom::Thread::DB_THREAD: + *out = metrics::CallStackProfileParams::Thread::DB_THREAD; + return true; + case metrics::mojom::Thread::GPU_MAIN_THREAD: + *out = metrics::CallStackProfileParams::Thread::GPU_MAIN_THREAD; + return true; + case metrics::mojom::Thread::RENDER_THREAD: + *out = metrics::CallStackProfileParams::Thread::RENDER_THREAD; + return true; + case metrics::mojom::Thread::UTILITY_THREAD: + *out = metrics::CallStackProfileParams::Thread::UTILITY_THREAD; + return true; + } + return false; + } +}; + +template <> +struct EnumTraits<metrics::mojom::Trigger, + metrics::CallStackProfileParams::Trigger> { + static metrics::mojom::Trigger ToMojom( + metrics::CallStackProfileParams::Trigger trigger) { + switch (trigger) { + case metrics::CallStackProfileParams::Trigger::UNKNOWN: + return metrics::mojom::Trigger::UNKNOWN; + case metrics::CallStackProfileParams::Trigger::PROCESS_STARTUP: + return metrics::mojom::Trigger::PROCESS_STARTUP; + case metrics::CallStackProfileParams::Trigger::JANKY_TASK: + return metrics::mojom::Trigger::JANKY_TASK; + case metrics::CallStackProfileParams::Trigger::THREAD_HUNG: + return metrics::mojom::Trigger::THREAD_HUNG; + } + NOTREACHED(); + return metrics::mojom::Trigger::UNKNOWN; + } + + static bool FromMojom(metrics::mojom::Trigger trigger, + metrics::CallStackProfileParams::Trigger* out) { + switch (trigger) { + case metrics::mojom::Trigger::UNKNOWN: + *out = metrics::CallStackProfileParams::Trigger::UNKNOWN; + return true; + case metrics::mojom::Trigger::PROCESS_STARTUP: + *out = metrics::CallStackProfileParams::Trigger::PROCESS_STARTUP; + return true; + case metrics::mojom::Trigger::JANKY_TASK: + *out = metrics::CallStackProfileParams::Trigger::JANKY_TASK; + return true; + case metrics::mojom::Trigger::THREAD_HUNG: + *out = metrics::CallStackProfileParams::Trigger::THREAD_HUNG; + return true; + } + return false; + } +}; + +template <> +struct StructTraits<metrics::mojom::CallStackProfileParamsDataView, + metrics::CallStackProfileParams> { + static metrics::CallStackProfileParams::Process process( + const metrics::CallStackProfileParams& params) { + return params.process; + } + static metrics::CallStackProfileParams::Thread thread( + const metrics::CallStackProfileParams& params) { + return params.thread; + } + static metrics::CallStackProfileParams::Trigger trigger( + const metrics::CallStackProfileParams& params) { + return params.trigger; + } + static metrics::CallStackProfileParams::SampleOrderingSpec ordering_spec( + const metrics::CallStackProfileParams& params) { + return params.ordering_spec; + } + + static bool Read(metrics::mojom::CallStackProfileParamsDataView data, + metrics::CallStackProfileParams* out) { + metrics::CallStackProfileParams::Process process; + metrics::CallStackProfileParams::Thread thread; + metrics::CallStackProfileParams::Trigger trigger; + metrics::CallStackProfileParams::SampleOrderingSpec ordering_spec; + if (!data.ReadProcess(&process) || !data.ReadThread(&thread) || + !data.ReadTrigger(&trigger) || !data.ReadOrderingSpec(&ordering_spec)) { + return false; + } + *out = metrics::CallStackProfileParams(process, thread, trigger, + ordering_spec); + return true; + } +}; + +template <> +struct EnumTraits<metrics::mojom::SampleOrderingSpec, + metrics::CallStackProfileParams::SampleOrderingSpec> { + + static metrics::mojom::SampleOrderingSpec ToMojom( + metrics::CallStackProfileParams::SampleOrderingSpec spec) { + switch (spec) { + case metrics::CallStackProfileParams::SampleOrderingSpec::MAY_SHUFFLE: + return metrics::mojom::SampleOrderingSpec::MAY_SHUFFLE; + case metrics::CallStackProfileParams::SampleOrderingSpec::PRESERVE_ORDER: + return metrics::mojom::SampleOrderingSpec::PRESERVE_ORDER; + } + NOTREACHED(); + return metrics::mojom::SampleOrderingSpec::MAY_SHUFFLE; + } + + static bool FromMojom( + metrics::mojom::SampleOrderingSpec spec, + metrics::CallStackProfileParams::SampleOrderingSpec* out) { + switch (spec) { + case metrics::mojom::SampleOrderingSpec::MAY_SHUFFLE: + *out = metrics::CallStackProfileParams::SampleOrderingSpec::MAY_SHUFFLE; + return true; + case metrics::mojom::SampleOrderingSpec::PRESERVE_ORDER: + *out = + metrics::CallStackProfileParams::SampleOrderingSpec::PRESERVE_ORDER; + return true; + } + return false; + } +}; + +} // mojo + +#endif // COMPONENTS_METRICS_CALL_STACK_PROFILE_STRUCT_TRAITS_H_ diff --git a/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc b/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc new file mode 100644 index 00000000000..14db487f452 --- /dev/null +++ b/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc @@ -0,0 +1,402 @@ +// Copyright 2016 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 <utility> + +#include "base/logging.h" +#include "base/macros.h" +#include "base/message_loop/message_loop.h" +#include "components/metrics/public/interfaces/call_stack_profile_collector_test.mojom.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace metrics { + +namespace { + +base::StackSamplingProfiler::CallStackProfile CreateProfile( + const std::vector<base::StackSamplingProfiler::Module>& modules, + const std::vector<base::StackSamplingProfiler::Sample>& samples, + base::TimeDelta profile_duration, + base::TimeDelta sampling_period) { + base::StackSamplingProfiler::CallStackProfile profile; + profile.modules = modules; + profile.samples = samples; + profile.profile_duration = profile_duration; + profile.sampling_period = sampling_period; + return profile; +} + +} + +class CallStackProfileCollectorTestImpl + : public mojom::CallStackProfileCollectorTest { + public: + explicit CallStackProfileCollectorTestImpl( + mojo::InterfaceRequest<mojom::CallStackProfileCollectorTest> request) + : binding_(this, std::move(request)) { + } + + // CallStackProfileCollectorTest: + void BounceFrame(const base::StackSamplingProfiler::Frame& in, + const BounceFrameCallback& callback) override { + callback.Run(in); + } + + void BounceModule(const base::StackSamplingProfiler::Module& in, + const BounceModuleCallback& callback) override { + callback.Run(in); + } + + void BounceProfile(const base::StackSamplingProfiler::CallStackProfile& in, + const BounceProfileCallback& callback) override { + callback.Run(in); + } + + void BounceTrigger(CallStackProfileParams::Trigger in, + const BounceTriggerCallback& callback) override { + callback.Run(in); + } + + void BounceProcess(CallStackProfileParams::Process in, + const BounceProcessCallback& callback) override { + callback.Run(in); + } + + void BounceThread(CallStackProfileParams::Thread in, + const BounceThreadCallback& callback) override { + callback.Run(in); + } + + void BounceSampleOrderingSpec( + CallStackProfileParams::SampleOrderingSpec in, + const BounceSampleOrderingSpecCallback& callback) override { + callback.Run(in); + } + + void BounceCallStackProfileParams( + const CallStackProfileParams& in, + const BounceCallStackProfileParamsCallback& callback) override { + callback.Run(in); + } + + private: + mojo::Binding<CallStackProfileCollectorTest> binding_; + + DISALLOW_COPY_AND_ASSIGN(CallStackProfileCollectorTestImpl); +}; + +class CallStackProfileStructTraitsTest : public testing::Test { + public: + CallStackProfileStructTraitsTest() : impl_(GetProxy(&proxy_)) {} + + protected: + base::MessageLoop message_loop_; + mojom::CallStackProfileCollectorTestPtr proxy_; + CallStackProfileCollectorTestImpl impl_; + + DISALLOW_COPY_AND_ASSIGN(CallStackProfileStructTraitsTest); +}; + +// Checks serialization/deserialization of Module fields. +TEST_F(CallStackProfileStructTraitsTest, Module) { + using Module = base::StackSamplingProfiler::Module; + + struct SerializeCase { + Module module; + bool expect_success; + }; + + const SerializeCase serialize_cases[] = { + // Null base address. + { + Module(0x0, "abcd", base::FilePath(base::FilePath::kCurrentDirectory)), + true + }, + // Non-null base address. + { + Module(0x10, "abcd", base::FilePath(base::FilePath::kCurrentDirectory)), + true + }, + // Base address with a bit set beyond 32 bits, when built for x64. + { + Module(1ULL << (sizeof(uintptr_t) * 8) * 3 / 4, "abcd", + base::FilePath(base::FilePath::kCurrentDirectory)), + true + }, + // Empty module id. + { + Module(0x10, "", base::FilePath(base::FilePath::kCurrentDirectory)), + true + }, + // Module id at the length limit. + { + Module(0x10, std::string(40, ' '), + base::FilePath(base::FilePath::kCurrentDirectory)), + true + }, + // Module id beyond the length limit. + { + Module(0x10, std::string(41, ' '), + base::FilePath(base::FilePath::kCurrentDirectory)), + false + }, + }; + + for (const SerializeCase& input : serialize_cases) { + Module output; + EXPECT_EQ(input.expect_success, + proxy_->BounceModule(input.module, &output)); + + if (!input.expect_success) + continue; + + EXPECT_EQ(input.module.base_address, output.base_address); + EXPECT_EQ(input.module.id, output.id); + EXPECT_EQ(input.module.filename, output.filename); + } +} + +// Checks serialization/deserialization of Frame fields. +TEST_F(CallStackProfileStructTraitsTest, Frame) { + using Frame = base::StackSamplingProfiler::Frame; + + const Frame serialize_cases[] = { + // Null instruction pointer. + Frame(0x0, 10), + // Non-null instruction pointer. + Frame(0x10, 10), + // Instruction pointer with a bit set beyond 32 bits, when built for x64. + Frame(1ULL << (sizeof(uintptr_t) * 8) * 3 / 4, 10), + // Zero module index. + Frame(0xabcd, 0), + // Non-zero module index. + Frame(0xabcd, 1), + // Non-zero module index. + Frame(0xabcd, 10), + // Unknown module index. + Frame(0xabcd, Frame::kUnknownModuleIndex), + }; + + for (const Frame& input : serialize_cases) { + Frame output; + EXPECT_TRUE(proxy_->BounceFrame(input, &output)); + + EXPECT_EQ(input.instruction_pointer, output.instruction_pointer); + EXPECT_EQ(input.module_index, output.module_index); + } +} + +// Checks serialization/deserialization of Profile fields, including validation +// of the Frame module_index field. +TEST_F(CallStackProfileStructTraitsTest, Profile) { + using Module = base::StackSamplingProfiler::Module; + using Frame = base::StackSamplingProfiler::Frame; + using Sample = base::StackSamplingProfiler::Sample; + using Profile = base::StackSamplingProfiler::CallStackProfile; + + struct SerializeCase { + Profile profile; + bool expect_success; + }; + + const SerializeCase serialize_cases[] = { + // Empty modules and samples. + { + CreateProfile(std::vector<Module>(), std::vector<Sample>(), + base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromSeconds(2)), + true + }, + // Non-empty modules and empty samples. + { + CreateProfile({ Module(0x4000, "a", base::FilePath()) }, + std::vector<Sample>(), + base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromSeconds(2)), + true + }, + // Valid values for modules and samples. + { + CreateProfile({ + Module(0x4000, "a", base::FilePath()), + Module(0x4100, "b", base::FilePath()), + }, + { + { + Frame(0x4010, 0), + Frame(0x4110, 1), + Frame(0x4110, Frame::kUnknownModuleIndex), + } + }, + base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromSeconds(2)), + true + }, + // Valid values for modules, but an out of range module index in the second + // sample. + { + CreateProfile({ + Module(0x4000, "a", base::FilePath()), + Module(0x4100, "b", base::FilePath()), + }, + { + { + Frame(0x4010, 0), + Frame(0x4110, 1), + Frame(0x4110, Frame::kUnknownModuleIndex), + }, + { + Frame(0x4010, 0), + Frame(0x4110, 2), + }, + }, + base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromSeconds(2)), + false + }, + }; + + for (const SerializeCase& input : serialize_cases) { + SCOPED_TRACE(&input - &serialize_cases[0]); + + Profile output; + EXPECT_EQ(input.expect_success, + proxy_->BounceProfile(input.profile, &output)); + + if (!input.expect_success) + continue; + + EXPECT_EQ(input.profile.modules, output.modules); + EXPECT_EQ(input.profile.samples, output.samples); + EXPECT_EQ(input.profile.profile_duration, output.profile_duration); + EXPECT_EQ(input.profile.sampling_period, output.sampling_period); + } +} + +// Checks serialization/deserialization of the process, including validation. +TEST_F(CallStackProfileStructTraitsTest, Process) { + using Process = CallStackProfileParams::Process; + + Process out; + + EXPECT_TRUE(proxy_->BounceProcess(Process::UNKNOWN_PROCESS, &out)); + EXPECT_EQ(Process::UNKNOWN_PROCESS, out); + + EXPECT_TRUE(proxy_->BounceProcess(Process::BROWSER_PROCESS, &out)); + EXPECT_EQ(Process::BROWSER_PROCESS, out); + + EXPECT_TRUE(proxy_->BounceProcess(Process::RENDERER_PROCESS, &out)); + EXPECT_EQ(Process::RENDERER_PROCESS, out); + + EXPECT_TRUE(proxy_->BounceProcess(Process::GPU_PROCESS, &out)); + EXPECT_EQ(Process::GPU_PROCESS, out); + + EXPECT_TRUE(proxy_->BounceProcess(Process::UTILITY_PROCESS, &out)); + EXPECT_EQ(Process::UTILITY_PROCESS, out); + + EXPECT_TRUE(proxy_->BounceProcess(Process::ZYGOTE_PROCESS, &out)); + EXPECT_EQ(Process::ZYGOTE_PROCESS, out); + + EXPECT_TRUE(proxy_->BounceProcess(Process::SANDBOX_HELPER_PROCESS, &out)); + EXPECT_EQ(Process::SANDBOX_HELPER_PROCESS, out); + + EXPECT_TRUE(proxy_->BounceProcess(Process::PPAPI_PLUGIN_PROCESS, &out)); + EXPECT_EQ(Process::PPAPI_PLUGIN_PROCESS, out); + + EXPECT_TRUE(proxy_->BounceProcess(Process::PPAPI_BROKER_PROCESS, &out)); + EXPECT_EQ(Process::PPAPI_BROKER_PROCESS, out); +} + +// Checks serialization/deserialization of the thread, including validation. +TEST_F(CallStackProfileStructTraitsTest, Thread) { + using Thread = CallStackProfileParams::Thread; + + Thread out; + + EXPECT_TRUE(proxy_->BounceThread(Thread::UI_THREAD, &out)); + EXPECT_EQ(Thread::UI_THREAD, out); + + EXPECT_TRUE(proxy_->BounceThread(Thread::FILE_THREAD, &out)); + EXPECT_EQ(Thread::FILE_THREAD, out); + + EXPECT_TRUE(proxy_->BounceThread(Thread::FILE_USER_BLOCKING_THREAD, &out)); + EXPECT_EQ(Thread::FILE_USER_BLOCKING_THREAD, out); + + EXPECT_TRUE(proxy_->BounceThread(Thread::PROCESS_LAUNCHER_THREAD, &out)); + EXPECT_EQ(Thread::PROCESS_LAUNCHER_THREAD, out); + + EXPECT_TRUE(proxy_->BounceThread(Thread::CACHE_THREAD, &out)); + EXPECT_EQ(Thread::CACHE_THREAD, out); + + EXPECT_TRUE(proxy_->BounceThread(Thread::IO_THREAD, &out)); + EXPECT_EQ(Thread::IO_THREAD, out); + + EXPECT_TRUE(proxy_->BounceThread(Thread::DB_THREAD, &out)); + EXPECT_EQ(Thread::DB_THREAD, out); + + EXPECT_TRUE(proxy_->BounceThread(Thread::GPU_MAIN_THREAD, &out)); + EXPECT_EQ(Thread::GPU_MAIN_THREAD, out); + + EXPECT_TRUE(proxy_->BounceThread(Thread::RENDER_THREAD, &out)); + EXPECT_EQ(Thread::RENDER_THREAD, out); + + EXPECT_TRUE(proxy_->BounceThread(Thread::UTILITY_THREAD, &out)); + EXPECT_EQ(Thread::UTILITY_THREAD, out); +} + +// Checks serialization/deserialization of the trigger, including validation. +TEST_F(CallStackProfileStructTraitsTest, Trigger) { + using Trigger = CallStackProfileParams::Trigger; + + Trigger out; + + EXPECT_TRUE(proxy_->BounceTrigger(Trigger::UNKNOWN, &out)); + EXPECT_EQ(Trigger::UNKNOWN, out); + + EXPECT_TRUE(proxy_->BounceTrigger(Trigger::PROCESS_STARTUP, &out)); + EXPECT_EQ(Trigger::PROCESS_STARTUP, out); + + EXPECT_TRUE(proxy_->BounceTrigger(Trigger::JANKY_TASK, &out)); + EXPECT_EQ(Trigger::JANKY_TASK, out); + + EXPECT_TRUE(proxy_->BounceTrigger(Trigger::THREAD_HUNG, &out)); + EXPECT_EQ(Trigger::THREAD_HUNG, out); +} + +// Checks serialization/deserialization of the SampleOrderingSpec, including +// validation. +TEST_F(CallStackProfileStructTraitsTest, SampleOrderingSpec) { + using SampleOrderingSpec = CallStackProfileParams::SampleOrderingSpec; + + SampleOrderingSpec out; + + EXPECT_TRUE(proxy_->BounceSampleOrderingSpec(SampleOrderingSpec::MAY_SHUFFLE, + &out)); + EXPECT_EQ(SampleOrderingSpec::MAY_SHUFFLE, out); + + EXPECT_TRUE(proxy_->BounceSampleOrderingSpec( + SampleOrderingSpec::PRESERVE_ORDER, + &out)); + EXPECT_EQ(SampleOrderingSpec::PRESERVE_ORDER, out); +} + +// Checks serialization/deserialization of the CallStackProfileParams. +TEST_F(CallStackProfileStructTraitsTest, CallStackProfileParams) { + CallStackProfileParams out; + + EXPECT_TRUE(proxy_->BounceCallStackProfileParams( + CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS, + CallStackProfileParams::UI_THREAD, + CallStackProfileParams::PROCESS_STARTUP, + CallStackProfileParams::PRESERVE_ORDER), + &out)); + + EXPECT_EQ(CallStackProfileParams::BROWSER_PROCESS, out.process); + EXPECT_EQ(CallStackProfileParams::UI_THREAD, out.thread); + EXPECT_EQ(CallStackProfileParams::PROCESS_STARTUP, out.trigger); + EXPECT_EQ(CallStackProfileParams::PRESERVE_ORDER, out.ordering_spec); +} + +} // namespace metrics diff --git a/chromium/components/metrics/public/cpp/typemaps.gni b/chromium/components/metrics/public/cpp/typemaps.gni new file mode 100644 index 00000000000..079917f788c --- /dev/null +++ b/chromium/components/metrics/public/cpp/typemaps.gni @@ -0,0 +1,5 @@ +# Copyright 2016 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. + +typemaps = [ "//components/metrics/public/cpp/call_stack_profile.typemap" ] diff --git a/chromium/components/metrics/public/interfaces/BUILD.gn b/chromium/components/metrics/public/interfaces/BUILD.gn new file mode 100644 index 00000000000..c6db51a6cf5 --- /dev/null +++ b/chromium/components/metrics/public/interfaces/BUILD.gn @@ -0,0 +1,26 @@ +# Copyright 2016 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. + +import("//mojo/public/tools/bindings/mojom.gni") + +mojom("call_stack_mojo_bindings") { + sources = [ + "call_stack_profile_collector.mojom", + ] + + deps = [ + "//mojo/common:common_custom_types", + ] +} + +mojom("call_stack_mojo_test_bindings") { + sources = [ + "call_stack_profile_collector_test.mojom", + ] + + deps = [ + ":call_stack_mojo_bindings", + "//mojo/common:common_custom_types", + ] +} diff --git a/chromium/components/metrics/public/interfaces/OWNERS b/chromium/components/metrics/public/interfaces/OWNERS new file mode 100644 index 00000000000..154435234ea --- /dev/null +++ b/chromium/components/metrics/public/interfaces/OWNERS @@ -0,0 +1,4 @@ +per-file *.mojom=set noparent +per-file *.mojom=file://ipc/SECURITY_OWNERS +per-file *_struct_traits*.*=set noparent +per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS diff --git a/chromium/components/metrics/public/interfaces/call_stack_profile_collector.mojom b/chromium/components/metrics/public/interfaces/call_stack_profile_collector.mojom new file mode 100644 index 00000000000..6c8f04216c6 --- /dev/null +++ b/chromium/components/metrics/public/interfaces/call_stack_profile_collector.mojom @@ -0,0 +1,81 @@ +// Copyright 2016 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. + +module metrics.mojom; + +import "mojo/common/common_custom_types.mojom"; + +// These structs mirror the corresponding types in base::StackSamplingProfiler. + +struct CallStackModule { + uint64 base_address; + string id; + mojo.common.mojom.FilePath filename; +}; + +struct CallStackFrame { + uint64 instruction_pointer; + uint64 module_index; +}; + +struct CallStackProfile { + array<CallStackModule> modules; + array<array<CallStackFrame>> samples; + mojo.common.mojom.TimeDelta profile_duration; + mojo.common.mojom.TimeDelta sampling_period; +}; + +enum Process { + UNKNOWN_PROCESS, + BROWSER_PROCESS, + RENDERER_PROCESS, + GPU_PROCESS, + UTILITY_PROCESS, + ZYGOTE_PROCESS, + SANDBOX_HELPER_PROCESS, + PPAPI_PLUGIN_PROCESS, + PPAPI_BROKER_PROCESS, +}; + +enum Thread { + UNKNOWN_THREAD, + + UI_THREAD, + FILE_THREAD, + FILE_USER_BLOCKING_THREAD, + PROCESS_LAUNCHER_THREAD, + CACHE_THREAD, + IO_THREAD, + DB_THREAD, + + GPU_MAIN_THREAD, + + RENDER_THREAD, + UTILITY_THREAD, +}; + +enum Trigger { + UNKNOWN, + PROCESS_STARTUP, + JANKY_TASK, + THREAD_HUNG, +}; + +enum SampleOrderingSpec { + MAY_SHUFFLE, + PRESERVE_ORDER, +}; + +struct CallStackProfileParams { + Process process; + Thread thread; + Trigger trigger; + SampleOrderingSpec ordering_spec; +}; + +interface CallStackProfileCollector { + Collect(CallStackProfileParams params, + mojo.common.mojom.TimeTicks start_timestamp, + array<CallStackProfile> profiles); +}; diff --git a/chromium/components/metrics/public/interfaces/call_stack_profile_collector_test.mojom b/chromium/components/metrics/public/interfaces/call_stack_profile_collector_test.mojom new file mode 100644 index 00000000000..2c7f1c1471a --- /dev/null +++ b/chromium/components/metrics/public/interfaces/call_stack_profile_collector_test.mojom @@ -0,0 +1,33 @@ +// Copyright 2016 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. + +module metrics.mojom; + +import "components/metrics/public/interfaces/call_stack_profile_collector.mojom"; + +interface CallStackProfileCollectorTest { + [Sync] + BounceFrame(CallStackFrame in) => (CallStackFrame out); + + [Sync] + BounceModule(CallStackModule in) => (CallStackModule out); + + [Sync] + BounceProfile(CallStackProfile in) => (CallStackProfile out); + + [Sync] + BounceProcess(Process in) => (Process out); + + [Sync] + BounceThread(Thread in) => (Thread out); + + [Sync] + BounceTrigger(Trigger in) => (Trigger out); + + [Sync] + BounceSampleOrderingSpec(SampleOrderingSpec in) => (SampleOrderingSpec out); + + [Sync] + BounceCallStackProfileParams(CallStackProfileParams in) => (CallStackProfileParams out); +}; diff --git a/chromium/components/metrics/serialization/serialization_utils_unittest.cc b/chromium/components/metrics/serialization/serialization_utils_unittest.cc index f3110166670..9d1e3602e34 100644 --- a/chromium/components/metrics/serialization/serialization_utils_unittest.cc +++ b/chromium/components/metrics/serialization/serialization_utils_unittest.cc @@ -22,7 +22,7 @@ class SerializationUtilsTest : public testing::Test { SerializationUtilsTest() { bool success = temporary_dir.CreateUniqueTempDir(); if (success) { - base::FilePath dir_path = temporary_dir.path(); + base::FilePath dir_path = temporary_dir.GetPath(); filename = dir_path.value() + "chromeossampletest"; filepath = base::FilePath(filename); } @@ -154,8 +154,8 @@ TEST_F(SerializationUtilsTest, WriteReadTest) { ScopedVector<MetricSample> vect; SerializationUtils::ReadAndTruncateMetricsFromFile(filename, &vect); ASSERT_EQ(vect.size(), size_t(5)); - for (int i = 0; i < 5; i++) { - ASSERT_TRUE(vect[0] != NULL); + for (MetricSample* sample : vect) { + ASSERT_NE(nullptr, sample); } EXPECT_TRUE(hist->IsEqual(*vect[0])); EXPECT_TRUE(crash->IsEqual(*vect[1])); diff --git a/chromium/components/metrics/stability_metrics_helper.cc b/chromium/components/metrics/stability_metrics_helper.cc index 7b771f9f4d5..8c33f7773e5 100644 --- a/chromium/components/metrics/stability_metrics_helper.cc +++ b/chromium/components/metrics/stability_metrics_helper.cc @@ -9,7 +9,7 @@ #include <vector> #include "base/logging.h" -#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros.h" #include "base/metrics/sparse_histogram.h" #include "build/build_config.h" #include "components/metrics/metrics_pref_names.h" @@ -95,6 +95,12 @@ void StabilityMetricsHelper::ProvideStabilityMetrics( local_state_->SetInteger(prefs::kStabilityRendererFailedLaunchCount, 0); } + count = local_state_->GetInteger(prefs::kStabilityRendererLaunchCount); + if (count) { + stability_proto->set_renderer_launch_count(count); + local_state_->SetInteger(prefs::kStabilityRendererLaunchCount, 0); + } + count = local_state_->GetInteger(prefs::kStabilityExtensionRendererCrashCount); if (count) { @@ -115,6 +121,13 @@ void StabilityMetricsHelper::ProvideStabilityMetrics( stability_proto->set_renderer_hang_count(count); local_state_->SetInteger(prefs::kStabilityRendererHangCount, 0); } + + count = + local_state_->GetInteger(prefs::kStabilityExtensionRendererLaunchCount); + if (count) { + stability_proto->set_extension_renderer_launch_count(count); + local_state_->SetInteger(prefs::kStabilityExtensionRendererLaunchCount, 0); + } } void StabilityMetricsHelper::ClearSavedStabilityMetrics() { @@ -124,10 +137,12 @@ void StabilityMetricsHelper::ClearSavedStabilityMetrics() { local_state_->SetInteger(prefs::kStabilityExtensionRendererCrashCount, 0); local_state_->SetInteger(prefs::kStabilityExtensionRendererFailedLaunchCount, 0); + local_state_->SetInteger(prefs::kStabilityExtensionRendererLaunchCount, 0); local_state_->SetInteger(prefs::kStabilityPageLoadCount, 0); local_state_->SetInteger(prefs::kStabilityRendererCrashCount, 0); local_state_->SetInteger(prefs::kStabilityRendererFailedLaunchCount, 0); local_state_->SetInteger(prefs::kStabilityRendererHangCount, 0); + local_state_->SetInteger(prefs::kStabilityRendererLaunchCount, 0); } // static @@ -137,10 +152,13 @@ void StabilityMetricsHelper::RegisterPrefs(PrefRegistrySimple* registry) { 0); registry->RegisterIntegerPref( prefs::kStabilityExtensionRendererFailedLaunchCount, 0); + registry->RegisterIntegerPref(prefs::kStabilityExtensionRendererLaunchCount, + 0); registry->RegisterIntegerPref(prefs::kStabilityPageLoadCount, 0); registry->RegisterIntegerPref(prefs::kStabilityRendererCrashCount, 0); registry->RegisterIntegerPref(prefs::kStabilityRendererFailedLaunchCount, 0); registry->RegisterIntegerPref(prefs::kStabilityRendererHangCount, 0); + registry->RegisterIntegerPref(prefs::kStabilityRendererLaunchCount, 0); registry->RegisterInt64Pref(prefs::kUninstallMetricsPageLoadCount, 0); } @@ -164,48 +182,73 @@ void StabilityMetricsHelper::LogRendererCrash(bool was_extension_process, int exit_code) { int histogram_type = was_extension_process ? RENDERER_TYPE_EXTENSION : RENDERER_TYPE_RENDERER; - if (status == base::TERMINATION_STATUS_PROCESS_CRASHED || - status == base::TERMINATION_STATUS_ABNORMAL_TERMINATION) { - if (was_extension_process) { - IncrementPrefValue(prefs::kStabilityExtensionRendererCrashCount); - - UMA_HISTOGRAM_SPARSE_SLOWLY("CrashExitCodes.Extension", - MapCrashExitCodeForHistogram(exit_code)); - } else { - IncrementPrefValue(prefs::kStabilityRendererCrashCount); - - UMA_HISTOGRAM_SPARSE_SLOWLY("CrashExitCodes.Renderer", - MapCrashExitCodeForHistogram(exit_code)); - } - - UMA_HISTOGRAM_ENUMERATION("BrowserRenderProcessHost.ChildCrashes", - histogram_type, RENDERER_TYPE_COUNT); - } else if (status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED) { - RecordChildKills(histogram_type); + + switch (status) { + case base::TERMINATION_STATUS_NORMAL_TERMINATION: + break; + case base::TERMINATION_STATUS_PROCESS_CRASHED: + case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: + case base::TERMINATION_STATUS_OOM: + if (was_extension_process) { + IncrementPrefValue(prefs::kStabilityExtensionRendererCrashCount); + + UMA_HISTOGRAM_SPARSE_SLOWLY("CrashExitCodes.Extension", + MapCrashExitCodeForHistogram(exit_code)); + } else { + IncrementPrefValue(prefs::kStabilityRendererCrashCount); + + UMA_HISTOGRAM_SPARSE_SLOWLY("CrashExitCodes.Renderer", + MapCrashExitCodeForHistogram(exit_code)); + } + + UMA_HISTOGRAM_ENUMERATION("BrowserRenderProcessHost.ChildCrashes", + histogram_type, RENDERER_TYPE_COUNT); + break; + case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: + RecordChildKills(histogram_type); + break; +#if defined(OS_ANDROID) + case base::TERMINATION_STATUS_OOM_PROTECTED: + // TODO(wfh): Check if this should be a Kill or a Crash on Android. + break; +#endif #if defined(OS_CHROMEOS) - } else if (status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM) { - RecordChildKills(histogram_type); - UMA_HISTOGRAM_ENUMERATION("BrowserRenderProcessHost.ChildKills.OOM", - was_extension_process ? 2 : 1, 3); - RecordMemoryStats(was_extension_process - ? RECORD_MEMORY_STATS_EXTENSIONS_OOM_KILLED - : RECORD_MEMORY_STATS_CONTENTS_OOM_KILLED); + case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM: + RecordChildKills(histogram_type); + UMA_HISTOGRAM_ENUMERATION("BrowserRenderProcessHost.ChildKills.OOM", + was_extension_process ? 2 : 1, 3); + RecordMemoryStats(was_extension_process + ? RECORD_MEMORY_STATS_EXTENSIONS_OOM_KILLED + : RECORD_MEMORY_STATS_CONTENTS_OOM_KILLED); + break; #endif - } else if (status == base::TERMINATION_STATUS_STILL_RUNNING) { - UMA_HISTOGRAM_ENUMERATION("BrowserRenderProcessHost.DisconnectedAlive", - histogram_type, RENDERER_TYPE_COUNT); - } else if (status == base::TERMINATION_STATUS_LAUNCH_FAILED) { - UMA_HISTOGRAM_ENUMERATION("BrowserRenderProcessHost.ChildLaunchFailures", - histogram_type, RENDERER_TYPE_COUNT); - UMA_HISTOGRAM_SPARSE_SLOWLY( - "BrowserRenderProcessHost.ChildLaunchFailureCodes", exit_code); - if (was_extension_process) - IncrementPrefValue(prefs::kStabilityExtensionRendererFailedLaunchCount); - else - IncrementPrefValue(prefs::kStabilityRendererFailedLaunchCount); + case base::TERMINATION_STATUS_STILL_RUNNING: + UMA_HISTOGRAM_ENUMERATION("BrowserRenderProcessHost.DisconnectedAlive", + histogram_type, RENDERER_TYPE_COUNT); + break; + case base::TERMINATION_STATUS_LAUNCH_FAILED: + UMA_HISTOGRAM_ENUMERATION("BrowserRenderProcessHost.ChildLaunchFailures", + histogram_type, RENDERER_TYPE_COUNT); + UMA_HISTOGRAM_SPARSE_SLOWLY( + "BrowserRenderProcessHost.ChildLaunchFailureCodes", exit_code); + if (was_extension_process) + IncrementPrefValue(prefs::kStabilityExtensionRendererFailedLaunchCount); + else + IncrementPrefValue(prefs::kStabilityRendererFailedLaunchCount); + break; + case base::TERMINATION_STATUS_MAX_ENUM: + NOTREACHED(); + break; } } +void StabilityMetricsHelper::LogRendererLaunched(bool was_extension_process) { + if (was_extension_process) + IncrementPrefValue(prefs::kStabilityExtensionRendererLaunchCount); + else + IncrementPrefValue(prefs::kStabilityRendererLaunchCount); +} + void StabilityMetricsHelper::IncrementPrefValue(const char* path) { int value = local_state_->GetInteger(path); local_state_->SetInteger(path, value + 1); diff --git a/chromium/components/metrics/stability_metrics_helper.h b/chromium/components/metrics/stability_metrics_helper.h index 42a9ca20217..87f8f400f19 100644 --- a/chromium/components/metrics/stability_metrics_helper.h +++ b/chromium/components/metrics/stability_metrics_helper.h @@ -36,10 +36,13 @@ class StabilityMetricsHelper { void LogLoadStarted(); // Records a renderer process crash. - void LogRendererCrash(bool was_exception_process, + void LogRendererCrash(bool was_extension_process, base::TerminationStatus status, int exit_code); + // Records that a new renderer process was successfully launched. + void LogRendererLaunched(bool was_extension_process); + // Records a renderer process hang. void LogRendererHang(); diff --git a/chromium/components/metrics/stability_metrics_helper_unittest.cc b/chromium/components/metrics/stability_metrics_helper_unittest.cc index d5aa9291a0c..018906d8a1c 100644 --- a/chromium/components/metrics/stability_metrics_helper_unittest.cc +++ b/chromium/components/metrics/stability_metrics_helper_unittest.cc @@ -5,6 +5,7 @@ #include "components/metrics/stability_metrics_helper.h" #include "base/macros.h" +#include "base/test/histogram_tester.h" #include "components/metrics/proto/system_profile.pb.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" @@ -15,6 +16,15 @@ namespace metrics { namespace { +enum RendererType { + RENDERER_TYPE_RENDERER = 1, + RENDERER_TYPE_EXTENSION, + // NOTE: Add new action types only immediately above this line. Also, + // make sure the enum list in tools/metrics/histograms/histograms.xml is + // updated with any change in here. + RENDERER_TYPE_COUNT +}; + class StabilityMetricsHelperTest : public testing::Test { protected: StabilityMetricsHelperTest() : prefs_(new TestingPrefServiceSimple) { @@ -52,6 +62,7 @@ TEST_F(StabilityMetricsHelperTest, BrowserChildProcessCrashed) { TEST_F(StabilityMetricsHelperTest, LogRendererCrash) { StabilityMetricsHelper helper(prefs()); + base::HistogramTester histogram_tester; // Crash and abnormal termination should increment renderer crash count. helper.LogRendererCrash(false, base::TERMINATION_STATUS_PROCESS_CRASHED, 1); @@ -59,6 +70,9 @@ TEST_F(StabilityMetricsHelperTest, LogRendererCrash) { helper.LogRendererCrash(false, base::TERMINATION_STATUS_ABNORMAL_TERMINATION, 1); + // OOM should increment renderer crash count. + helper.LogRendererCrash(false, base::TERMINATION_STATUS_OOM, 1); + // Kill does not increment renderer crash count. helper.LogRendererCrash(false, base::TERMINATION_STATUS_PROCESS_WAS_KILLED, 1); @@ -72,7 +86,7 @@ TEST_F(StabilityMetricsHelperTest, LogRendererCrash) { // be executed immediately. helper.ProvideStabilityMetrics(&system_profile); - EXPECT_EQ(2, system_profile.stability().renderer_crash_count()); + EXPECT_EQ(3, system_profile.stability().renderer_crash_count()); EXPECT_EQ(1, system_profile.stability().renderer_failed_launch_count()); EXPECT_EQ(0, system_profile.stability().extension_renderer_crash_count()); @@ -81,6 +95,9 @@ TEST_F(StabilityMetricsHelperTest, LogRendererCrash) { // Crash and abnormal termination should increment extension crash count. helper.LogRendererCrash(true, base::TERMINATION_STATUS_PROCESS_CRASHED, 1); + // OOM should increment extension renderer crash count. + helper.LogRendererCrash(true, base::TERMINATION_STATUS_OOM, 1); + // Failed launch increments extension failed launch count. helper.LogRendererCrash(true, base::TERMINATION_STATUS_LAUNCH_FAILED, 1); @@ -88,9 +105,36 @@ TEST_F(StabilityMetricsHelperTest, LogRendererCrash) { helper.ProvideStabilityMetrics(&system_profile); EXPECT_EQ(0, system_profile.stability().renderer_crash_count()); - EXPECT_EQ(1, system_profile.stability().extension_renderer_crash_count()); + EXPECT_EQ(2, system_profile.stability().extension_renderer_crash_count()); EXPECT_EQ( 1, system_profile.stability().extension_renderer_failed_launch_count()); + + // TERMINATION_STATUS_PROCESS_CRASHED, TERMINATION_STATUS_ABNORMAL_TERMINATION + // and TERMINATION_STATUS_OOM = 3. + histogram_tester.ExpectUniqueSample("CrashExitCodes.Renderer", 1, 3); + histogram_tester.ExpectBucketCount("BrowserRenderProcessHost.ChildCrashes", + RENDERER_TYPE_RENDERER, 3); + + // TERMINATION_STATUS_PROCESS_CRASHED and TERMINATION_STATUS_OOM = 2. + histogram_tester.ExpectUniqueSample("CrashExitCodes.Extension", 1, 2); + histogram_tester.ExpectBucketCount("BrowserRenderProcessHost.ChildCrashes", + RENDERER_TYPE_EXTENSION, 2); + + // One launch failure each. + histogram_tester.ExpectBucketCount( + "BrowserRenderProcessHost.ChildLaunchFailures", RENDERER_TYPE_RENDERER, + 1); + histogram_tester.ExpectBucketCount( + "BrowserRenderProcessHost.ChildLaunchFailures", RENDERER_TYPE_EXTENSION, + 1); + histogram_tester.ExpectBucketCount( + "BrowserRenderProcessHost.ChildLaunchFailureCodes", 1, 2); + + // TERMINATION_STATUS_PROCESS_WAS_KILLED for a renderer. + histogram_tester.ExpectBucketCount("BrowserRenderProcessHost.ChildKills", + RENDERER_TYPE_RENDERER, 1); + histogram_tester.ExpectBucketCount("BrowserRenderProcessHost.ChildKills", + RENDERER_TYPE_EXTENSION, 0); } } // namespace metrics diff --git a/chromium/components/metrics/system_memory_stats_recorder_win.cc b/chromium/components/metrics/system_memory_stats_recorder_win.cc index 539d80ffb1b..7d011821a4d 100644 --- a/chromium/components/metrics/system_memory_stats_recorder_win.cc +++ b/chromium/components/metrics/system_memory_stats_recorder_win.cc @@ -23,7 +23,7 @@ void RecordMemoryStats(RecordMemoryStatsType type) { switch (type) { case RECORD_MEMORY_STATS_TAB_DISCARDED: { UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Stats.Win.MemoryLoad", - mem_status.dwMemoryLoad, 0, 100, 101); + mem_status.dwMemoryLoad, 1, 100, 101); UMA_HISTOGRAM_LARGE_MEMORY_MB("Memory.Stats.Win.TotalPhys2", mem_status.ullTotalPhys / kMBytes); UMA_HISTOGRAM_LARGE_MEMORY_MB("Memory.Stats.Win.AvailPhys2", diff --git a/chromium/components/metrics/test_metrics_service_client.cc b/chromium/components/metrics/test_metrics_service_client.cc index 643880b6582..7c7ccc68e07 100644 --- a/chromium/components/metrics/test_metrics_service_client.cc +++ b/chromium/components/metrics/test_metrics_service_client.cc @@ -31,13 +31,6 @@ void TestMetricsServiceClient::SetMetricsClientId( client_id_ = client_id; } -void TestMetricsServiceClient::OnRecordingDisabled() { -} - -bool TestMetricsServiceClient::IsOffTheRecordSessionActive() { - return false; -} - int32_t TestMetricsServiceClient::GetProduct() { return product_; } diff --git a/chromium/components/metrics/test_metrics_service_client.h b/chromium/components/metrics/test_metrics_service_client.h index e935f4a8792..355e7c7c5ae 100644 --- a/chromium/components/metrics/test_metrics_service_client.h +++ b/chromium/components/metrics/test_metrics_service_client.h @@ -26,8 +26,6 @@ class TestMetricsServiceClient : public MetricsServiceClient { // MetricsServiceClient: metrics::MetricsService* GetMetricsService() override; void SetMetricsClientId(const std::string& client_id) override; - void OnRecordingDisabled() override; - bool IsOffTheRecordSessionActive() override; int32_t GetProduct() override; std::string GetApplicationLocale() override; bool GetBrand(std::string* brand_code) override; diff --git a/chromium/components/metrics/url_constants.cc b/chromium/components/metrics/url_constants.cc index 87e721e85e7..4a744d56aff 100644 --- a/chromium/components/metrics/url_constants.cc +++ b/chromium/components/metrics/url_constants.cc @@ -8,7 +8,7 @@ namespace metrics { -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_IOS) const char kDefaultMetricsServerUrl[] = "https://clientservices.googleapis.com/uma/v2"; #else |