1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/metrics/stability_metrics_provider.h"
6
7 #include <string>
8
9 #include "base/logging.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "build/build_config.h"
12 #include "components/metrics/metrics_pref_names.h"
13 #include "components/metrics/stability_metrics_helper.h"
14 #include "components/prefs/pref_registry_simple.h"
15 #include "components/prefs/pref_service.h"
16 #include "components/prefs/scoped_user_pref_update.h"
17 #include "third_party/metrics_proto/system_profile.pb.h"
18
19 #if BUILDFLAG(IS_ANDROID)
20 #include "base/android/build_info.h"
21 #endif
22 #if BUILDFLAG(IS_WIN)
23 #include "components/metrics/system_session_analyzer/system_session_analyzer_win.h"
24 #endif
25
26 namespace metrics {
27
28 namespace {
29
30 #if BUILDFLAG(IS_ANDROID)
HasGmsCoreVersionChanged(PrefService * local_state)31 bool HasGmsCoreVersionChanged(PrefService* local_state) {
32 std::string previous_version =
33 local_state->GetString(prefs::kStabilityGmsCoreVersion);
34 std::string current_version =
35 base::android::BuildInfo::GetInstance()->gms_version_code();
36
37 // If the last version is empty, treat it as consistent.
38 if (previous_version.empty())
39 return false;
40
41 return previous_version != current_version;
42 }
43
UpdateGmsCoreVersionPref(PrefService * local_state)44 void UpdateGmsCoreVersionPref(PrefService* local_state) {
45 std::string current_version =
46 base::android::BuildInfo::GetInstance()->gms_version_code();
47 local_state->SetString(prefs::kStabilityGmsCoreVersion, current_version);
48 }
49 #endif
50
51 } // namespace
52
StabilityMetricsProvider(PrefService * local_state)53 StabilityMetricsProvider::StabilityMetricsProvider(PrefService* local_state)
54 : local_state_(local_state) {}
55
56 StabilityMetricsProvider::~StabilityMetricsProvider() = default;
57
58 // static
RegisterPrefs(PrefRegistrySimple * registry)59 void StabilityMetricsProvider::RegisterPrefs(PrefRegistrySimple* registry) {
60 registry->RegisterIntegerPref(prefs::kStabilityFileMetricsUnsentFilesCount,
61 0);
62 registry->RegisterIntegerPref(prefs::kStabilityFileMetricsUnsentSamplesCount,
63 0);
64
65 #if BUILDFLAG(IS_ANDROID)
66 registry->RegisterIntegerPref(prefs::kStabilityLaunchCount, 0);
67 registry->RegisterStringPref(prefs::kStabilityGmsCoreVersion, "");
68 registry->RegisterIntegerPref(prefs::kStabilityCrashCountDueToGmsCoreUpdate,
69 0);
70 #endif
71 #if BUILDFLAG(IS_WIN)
72 registry->RegisterIntegerPref(prefs::kStabilitySystemCrashCount, 0);
73 #endif
74 }
75
Init()76 void StabilityMetricsProvider::Init() {
77 #if BUILDFLAG(IS_ANDROID)
78 // This method has to be called after HasGmsCoreVersionChanged() to avoid
79 // overwriting thie result.
80 UpdateGmsCoreVersionPref(local_state_);
81 #endif
82 }
83
ClearSavedStabilityMetrics()84 void StabilityMetricsProvider::ClearSavedStabilityMetrics() {
85 // The 0 is a valid value for the below prefs, clears pref instead
86 // of setting to default value.
87 local_state_->ClearPref(prefs::kStabilityFileMetricsUnsentFilesCount);
88 local_state_->ClearPref(prefs::kStabilityFileMetricsUnsentSamplesCount);
89
90 #if BUILDFLAG(IS_ANDROID)
91 local_state_->SetInteger(prefs::kStabilityLaunchCount, 0);
92 #endif
93 #if BUILDFLAG(IS_WIN)
94 local_state_->SetInteger(prefs::kStabilitySystemCrashCount, 0);
95 #endif
96 }
97
ProvideStabilityMetrics(SystemProfileProto * system_profile)98 void StabilityMetricsProvider::ProvideStabilityMetrics(
99 SystemProfileProto* system_profile) {
100 #if BUILDFLAG(IS_ANDROID)
101 SystemProfileProto::Stability* stability =
102 system_profile->mutable_stability();
103
104 int pref_value = 0;
105 if (GetAndClearPrefValue(prefs::kStabilityLaunchCount, &pref_value))
106 stability->set_launch_count(pref_value);
107 if (GetAndClearPrefValue(prefs::kStabilityCrashCountDueToGmsCoreUpdate,
108 &pref_value)) {
109 stability->set_crash_count_due_to_gms_core_update(pref_value);
110 }
111 #endif
112
113 if (local_state_->HasPrefPath(prefs::kStabilityFileMetricsUnsentFilesCount)) {
114 UMA_STABILITY_HISTOGRAM_COUNTS_100(
115 "Stability.Internals.FileMetricsProvider.BrowserMetrics."
116 "UnsentFilesCount",
117 local_state_->GetInteger(prefs::kStabilityFileMetricsUnsentFilesCount));
118 local_state_->ClearPref(prefs::kStabilityFileMetricsUnsentFilesCount);
119 }
120
121 if (local_state_->HasPrefPath(
122 prefs::kStabilityFileMetricsUnsentSamplesCount)) {
123 UMA_STABILITY_HISTOGRAM_CUSTOM_COUNTS(
124 "Stability.Internals.FileMetricsProvider.BrowserMetrics."
125 "UnsentSamplesCount",
126 local_state_->GetInteger(
127 prefs::kStabilityFileMetricsUnsentSamplesCount),
128 0, 1000000, 50);
129 local_state_->ClearPref(prefs::kStabilityFileMetricsUnsentSamplesCount);
130 }
131
132 #if BUILDFLAG(IS_WIN)
133 int pref_value = 0;
134 if (GetAndClearPrefValue(prefs::kStabilitySystemCrashCount, &pref_value)) {
135 UMA_STABILITY_HISTOGRAM_COUNTS_100("Stability.Internals.SystemCrashCount",
136 pref_value);
137 }
138 #endif
139 }
140
LogCrash(base::Time last_live_timestamp)141 void StabilityMetricsProvider::LogCrash(base::Time last_live_timestamp) {
142 #if BUILDFLAG(IS_ANDROID)
143 // On Android, if there is an update for GMS Core when Chrome is running,
144 // Chrome will be killed, counting as a crash. This is expected and should not
145 // be counted in stability crash counts. Thus these crashes are added to a
146 // specific bucket for crashes caused by GMS Core updates.
147 if (HasGmsCoreVersionChanged(local_state_)) {
148 IncrementPrefValue(prefs::kStabilityCrashCountDueToGmsCoreUpdate);
149 return;
150 }
151 #endif
152 StabilityMetricsHelper::RecordStabilityEvent(
153 StabilityEventType::kBrowserCrash);
154
155 #if BUILDFLAG(IS_WIN)
156 MaybeLogSystemCrash(last_live_timestamp);
157 #endif
158 }
159
LogLaunch()160 void StabilityMetricsProvider::LogLaunch() {
161 #if BUILDFLAG(IS_ANDROID)
162 IncrementPrefValue(prefs::kStabilityLaunchCount);
163 #endif
164 StabilityMetricsHelper::RecordStabilityEvent(StabilityEventType::kLaunch);
165 }
166
167 #if BUILDFLAG(IS_WIN)
IsUncleanSystemSession(base::Time last_live_timestamp)168 bool StabilityMetricsProvider::IsUncleanSystemSession(
169 base::Time last_live_timestamp) {
170 DCHECK_NE(base::Time(), last_live_timestamp);
171 // There's a non-null last live timestamp, see if this occurred in
172 // a Windows system session that ended uncleanly. The expectation is that
173 // |last_live_timestamp| will have occurred in the immediately previous system
174 // session, but if the system has been restarted many times since Chrome last
175 // ran, that's not necessarily true. Log traversal can be expensive, so we
176 // limit the analyzer to reaching back three previous system sessions to bound
177 // the cost of the traversal.
178 SystemSessionAnalyzer analyzer(3);
179
180 SystemSessionAnalyzer::Status status =
181 analyzer.IsSessionUnclean(last_live_timestamp);
182
183 return status == SystemSessionAnalyzer::UNCLEAN;
184 }
185
MaybeLogSystemCrash(base::Time last_live_timestamp)186 void StabilityMetricsProvider::MaybeLogSystemCrash(
187 base::Time last_live_timestamp) {
188 if (last_live_timestamp != base::Time() &&
189 IsUncleanSystemSession(last_live_timestamp)) {
190 IncrementPrefValue(prefs::kStabilitySystemCrashCount);
191 }
192 }
193 #endif
194
IncrementPrefValue(const char * path)195 void StabilityMetricsProvider::IncrementPrefValue(const char* path) {
196 int value = local_state_->GetInteger(path);
197 local_state_->SetInteger(path, value + 1);
198 }
199
GetAndClearPrefValue(const char * path,int * value)200 int StabilityMetricsProvider::GetAndClearPrefValue(const char* path,
201 int* value) {
202 *value = local_state_->GetInteger(path);
203 if (*value != 0)
204 local_state_->SetInteger(path, 0);
205 return *value;
206 }
207
208 } // namespace metrics
209