| OLD | NEW |
| 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/browser_watcher/watcher_metrics_provider_win.h" | 5 #include "components/browser_watcher/watcher_metrics_provider_win.h" |
| 6 | 6 |
| 7 #include <limits> | 7 #include <limits> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/metrics/sparse_histogram.h" | 10 #include "base/metrics/sparse_histogram.h" |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 138 LONG res = regkey.DeleteValue(event_name.c_str()); | 138 LONG res = regkey.DeleteValue(event_name.c_str()); |
| 139 if (res != ERROR_SUCCESS) { | 139 if (res != ERROR_SUCCESS) { |
| 140 LOG(ERROR) << "Failed to delete value " << event_name; | 140 LOG(ERROR) << "Failed to delete value " << event_name; |
| 141 return; | 141 return; |
| 142 } | 142 } |
| 143 } | 143 } |
| 144 | 144 |
| 145 events_out->swap(events); | 145 events_out->swap(events); |
| 146 } | 146 } |
| 147 | 147 |
| 148 void RecordSingleExitFunnel(base::win::RegKey* parent_key, | 148 void MaybeRecordSingleExitFunnel(base::win::RegKey* parent_key, |
| 149 const base::char16* name) { | 149 const base::char16* name, |
| 150 bool report) { |
| 150 std::vector<std::pair<base::string16, int64>> events; | 151 std::vector<std::pair<base::string16, int64>> events; |
| 151 ReadSingleExitFunnel(parent_key, name, &events); | 152 ReadSingleExitFunnel(parent_key, name, &events); |
| 153 if (!report) |
| 154 return; |
| 152 | 155 |
| 153 // Find the earliest event time. | 156 // Find the earliest event time. |
| 154 int64 min_time = std::numeric_limits<int64>::max(); | 157 int64 min_time = std::numeric_limits<int64>::max(); |
| 155 for (size_t i = 0; i < events.size(); ++i) | 158 for (size_t i = 0; i < events.size(); ++i) |
| 156 min_time = std::min(min_time, events[i].second); | 159 min_time = std::min(min_time, events[i].second); |
| 157 | 160 |
| 158 // Record the exit funnel event times in a sparse stability histogram. | 161 // Record the exit funnel event times in a sparse stability histogram. |
| 159 for (size_t i = 0; i < events.size(); ++i) { | 162 for (size_t i = 0; i < events.size(); ++i) { |
| 160 std::string histogram_name( | 163 std::string histogram_name( |
| 161 WatcherMetricsProviderWin::kExitFunnelHistogramPrefix); | 164 WatcherMetricsProviderWin::kExitFunnelHistogramPrefix); |
| 162 histogram_name.append(base::WideToUTF8(events[i].first)); | 165 histogram_name.append(base::WideToUTF8(events[i].first)); |
| 163 base::TimeDelta event_time = | 166 base::TimeDelta event_time = |
| 164 base::Time::FromInternalValue(events[i].second) - | 167 base::Time::FromInternalValue(events[i].second) - |
| 165 base::Time::FromInternalValue(min_time); | 168 base::Time::FromInternalValue(min_time); |
| 166 base::HistogramBase* histogram = | 169 base::HistogramBase* histogram = |
| 167 base::SparseHistogram::FactoryGet( | 170 base::SparseHistogram::FactoryGet( |
| 168 histogram_name.c_str(), | 171 histogram_name.c_str(), |
| 169 base::HistogramBase::kUmaStabilityHistogramFlag); | 172 base::HistogramBase::kUmaStabilityHistogramFlag); |
| 170 | 173 |
| 171 // Record the time rounded up to the nearest millisecond. | 174 // Record the time rounded up to the nearest millisecond. |
| 172 histogram->Add(event_time.InMillisecondsRoundedUp()); | 175 histogram->Add(event_time.InMillisecondsRoundedUp()); |
| 173 } | 176 } |
| 174 } | 177 } |
| 175 | 178 |
| 176 void RecordExitFunnels(const base::string16& registry_path) { | 179 void MaybeRecordExitFunnels(const base::string16& registry_path, bool report) { |
| 177 base::win::RegistryKeyIterator it(HKEY_CURRENT_USER, registry_path.c_str()); | 180 base::win::RegistryKeyIterator it(HKEY_CURRENT_USER, registry_path.c_str()); |
| 178 if (!it.Valid()) | 181 if (!it.Valid()) |
| 179 return; | 182 return; |
| 180 | 183 |
| 181 // Exit early if no work to do. | 184 // Exit early if no work to do. |
| 182 if (it.SubkeyCount() == 0) | 185 if (it.SubkeyCount() == 0) |
| 183 return; | 186 return; |
| 184 | 187 |
| 185 // Open the key we use for deletion preemptively to prevent reporting | 188 // Open the key we use for deletion preemptively to prevent reporting |
| 186 // multiple times on permission problems. | 189 // multiple times on permission problems. |
| 187 base::win::RegKey key(HKEY_CURRENT_USER, | 190 base::win::RegKey key(HKEY_CURRENT_USER, |
| 188 registry_path.c_str(), | 191 registry_path.c_str(), |
| 189 KEY_QUERY_VALUE); | 192 KEY_QUERY_VALUE); |
| 190 if (!key.Valid()) { | 193 if (!key.Valid()) { |
| 191 LOG(ERROR) << "Failed to open " << registry_path << " for writing."; | 194 LOG(ERROR) << "Failed to open " << registry_path << " for writing."; |
| 192 return; | 195 return; |
| 193 } | 196 } |
| 194 | 197 |
| 195 std::vector<base::string16> to_delete; | 198 std::vector<base::string16> to_delete; |
| 196 for (; it.Valid(); ++it) { | 199 for (; it.Valid(); ++it) { |
| 197 // Defer reporting on still-live processes. | 200 // Defer reporting on still-live processes. |
| 198 if (IsDeadProcess(it.Name())) { | 201 if (IsDeadProcess(it.Name())) { |
| 199 RecordSingleExitFunnel(&key, it.Name()); | 202 MaybeRecordSingleExitFunnel(&key, it.Name(), report); |
| 200 to_delete.push_back(it.Name()); | 203 to_delete.push_back(it.Name()); |
| 201 } | 204 } |
| 202 } | 205 } |
| 203 | 206 |
| 204 for (size_t i = 0; i < to_delete.size(); ++i) { | 207 for (size_t i = 0; i < to_delete.size(); ++i) { |
| 205 LONG res = key.DeleteEmptyKey(to_delete[i].c_str()); | 208 LONG res = key.DeleteEmptyKey(to_delete[i].c_str()); |
| 206 if (res != ERROR_SUCCESS) | 209 if (res != ERROR_SUCCESS) |
| 207 LOG(ERROR) << "Failed to delete key " << to_delete[i]; | 210 LOG(ERROR) << "Failed to delete key " << to_delete[i]; |
| 208 } | 211 } |
| 209 } | 212 } |
| 210 | 213 |
| 211 } // namespace | 214 } // namespace |
| 212 | 215 |
| 213 const char WatcherMetricsProviderWin::kBrowserExitCodeHistogramName[] = | 216 const char WatcherMetricsProviderWin::kBrowserExitCodeHistogramName[] = |
| 214 "Stability.BrowserExitCodes"; | 217 "Stability.BrowserExitCodes"; |
| 215 const char WatcherMetricsProviderWin::kExitFunnelHistogramPrefix[] = | 218 const char WatcherMetricsProviderWin::kExitFunnelHistogramPrefix[] = |
| 216 "Stability.ExitFunnel."; | 219 "Stability.ExitFunnel."; |
| 217 | 220 |
| 218 WatcherMetricsProviderWin::WatcherMetricsProviderWin( | 221 WatcherMetricsProviderWin::WatcherMetricsProviderWin( |
| 219 const base::char16* registry_path) : registry_path_(registry_path) { | 222 const base::char16* registry_path, bool report_exit_funnels) : |
| 223 registry_path_(registry_path), |
| 224 report_exit_funnels_(report_exit_funnels) { |
| 220 } | 225 } |
| 221 | 226 |
| 222 WatcherMetricsProviderWin::~WatcherMetricsProviderWin() { | 227 WatcherMetricsProviderWin::~WatcherMetricsProviderWin() { |
| 223 } | 228 } |
| 224 | 229 |
| 225 void WatcherMetricsProviderWin::ProvideStabilityMetrics( | 230 void WatcherMetricsProviderWin::ProvideStabilityMetrics( |
| 226 metrics::SystemProfileProto* /* system_profile_proto */) { | 231 metrics::SystemProfileProto* /* system_profile_proto */) { |
| 227 // Note that if there are multiple instances of Chrome running in the same | 232 // Note that if there are multiple instances of Chrome running in the same |
| 228 // user account, there's a small race that will double-report the exit codes | 233 // user account, there's a small race that will double-report the exit codes |
| 229 // from both/multiple instances. This ought to be vanishingly rare and will | 234 // from both/multiple instances. This ought to be vanishingly rare and will |
| 230 // only manifest as low-level "random" noise. To work around this it would be | 235 // only manifest as low-level "random" noise. To work around this it would be |
| 231 // necessary to implement some form of global locking, which is not worth it | 236 // necessary to implement some form of global locking, which is not worth it |
| 232 // here. | 237 // here. |
| 233 RecordExitCodes(registry_path_); | 238 RecordExitCodes(registry_path_); |
| 234 RecordExitFunnels(registry_path_); | 239 MaybeRecordExitFunnels(registry_path_, report_exit_funnels_); |
| 235 } | 240 } |
| 236 | 241 |
| 237 } // namespace browser_watcher | 242 } // namespace browser_watcher |
| OLD | NEW |