| // 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/browser_watcher/crash_reporting_metrics_win.h" |
| |
| #include <algorithm> |
| #include "base/atomicops.h" |
| #include "base/guid.h" |
| #include "base/logging.h" |
| #include "base/strings/safe_sprintf.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/win/registry.h" |
| |
| namespace browser_watcher { |
| |
| namespace { |
| |
| // The prior implementation used 0 and 1. Its data collection is inconsistent |
| // with ours and it's best to just ignore its records. So we start at 2. |
| const DWORD kCrashDumpAttempt = 2; |
| const DWORD kDumpWithoutCrashAttempt = 3; |
| const DWORD kCrashDumpSuccess = 4; |
| const DWORD kCrashDumpFailure = 5; |
| const DWORD kDumpWithoutCrashSuccess = 6; |
| const DWORD kDumpWithoutCrashFailure = 7; |
| |
| // A prefix intended to avoid registry value name collisions with other |
| // processes (or modules within a single process). It is not necessary for the |
| // reading and writing instances to share the prefix value. This array is sized |
| // to hold a null-terminated string as generated by base::GenerateGUID. |
| char g_unique_prefix[36 + 1] = {0}; |
| |
| // An atomic counter intended to make each registry value name unique within |
| // this process and module. |
| base::subtle::Atomic32 g_record_count = 0; |
| |
| // The length of a null-terminated string consisting of "{GUID}-{COUNT}". |
| const size_t kValueNameSize = 36 + 1 + 8 + 1; |
| |
| void WriteValue(const base::string16& key_path, DWORD value) { |
| // Generate the final value name we'll use (appends the crash number to the |
| // base value name). |
| char value_name_ascii[kValueNameSize] = ""; |
| base::char16 value_name_utf16[kValueNameSize] = L""; |
| |
| if (base::strings::SafeSPrintf( |
| value_name_ascii, "%s-%x", g_unique_prefix, |
| base::subtle::NoBarrier_AtomicIncrement(&g_record_count, 1)) <= 0) { |
| NOTREACHED(); |
| } else { |
| // Since it's an ASCII string, the UTF-16 form is identical with leading 0 |
| // bytes. We're avoiding unnecessary heap operations since we're running in |
| // a compromised process. |
| std::copy(value_name_ascii, value_name_ascii + kValueNameSize, |
| value_name_utf16); |
| |
| base::win::RegKey reg_key; |
| if (reg_key.Create(HKEY_CURRENT_USER, key_path.c_str(), KEY_SET_VALUE) != |
| ERROR_SUCCESS) { |
| NOTREACHED(); |
| } else { |
| reg_key.WriteValue(value_name_utf16, value); |
| } |
| } |
| |
| } |
| |
| } // namespace |
| |
| CrashReportingMetrics::CrashReportingMetrics( |
| const base::string16& registry_path) |
| : registry_path_(registry_path) { |
| if (g_unique_prefix[0] == 0) { |
| std::string guid = base::GenerateGUID(); |
| // It seems reasonable to assume that the worst possible outcome of two |
| // separate threads trying to do the following would be to store a GUID |
| // value that is a hybrid of the two intended values. Hence we can avoid any |
| // thread-safety caveats in our public API. |
| size_t copied_size = base::strlcpy(g_unique_prefix, guid.c_str(), |
| arraysize(g_unique_prefix)); |
| DCHECK_EQ(copied_size, guid.length()); |
| } |
| } |
| |
| void CrashReportingMetrics::RecordCrashDumpAttempt() { |
| WriteValue(registry_path_, kCrashDumpAttempt); |
| } |
| |
| void CrashReportingMetrics::RecordDumpWithoutCrashAttempt() { |
| WriteValue(registry_path_, kDumpWithoutCrashAttempt); |
| } |
| void CrashReportingMetrics::RecordCrashDumpAttemptResult(bool succeeded) { |
| WriteValue(registry_path_, succeeded ? kCrashDumpSuccess : kCrashDumpFailure); |
| } |
| void CrashReportingMetrics::RecordDumpWithoutCrashAttemptResult( |
| bool succeeded) { |
| WriteValue(registry_path_, |
| succeeded ? kDumpWithoutCrashSuccess : kDumpWithoutCrashFailure); |
| } |
| |
| CrashReportingMetrics::Values CrashReportingMetrics::RetrieveAndResetMetrics() { |
| Values values = {0}; |
| |
| // Open the registry key for iteration. |
| base::win::RegKey regkey; |
| if (regkey.Open(HKEY_CURRENT_USER, registry_path_.c_str(), |
| KEY_QUERY_VALUE | KEY_SET_VALUE) != ERROR_SUCCESS) { |
| return values; |
| } |
| |
| // Track a list of values to delete. We don't modify the registry key while |
| // we're iterating over its values. |
| typedef std::vector<base::string16> StringVector; |
| StringVector to_delete; |
| |
| // Iterate over the values in the key counting dumps with and without crashes. |
| // We directly walk the values instead of using RegistryValueIterator in order |
| // to read all of the values as DWORDS instead of strings. |
| base::string16 name; |
| DWORD value = 0; |
| for (int i = regkey.GetValueCount() - 1; i >= 0; --i) { |
| if (regkey.GetValueNameAt(i, &name) == ERROR_SUCCESS && |
| regkey.ReadValueDW(name.c_str(), &value) == ERROR_SUCCESS) { |
| to_delete.push_back(name); |
| switch (value) { |
| case kCrashDumpAttempt: |
| ++values.crash_dump_attempts; |
| break; |
| case kCrashDumpSuccess: |
| ++values.successful_crash_dumps; |
| break; |
| case kCrashDumpFailure: |
| ++values.failed_crash_dumps; |
| break; |
| case kDumpWithoutCrashAttempt: |
| ++values.dump_without_crash_attempts; |
| break; |
| case kDumpWithoutCrashSuccess: |
| ++values.successful_dumps_without_crash; |
| break; |
| case kDumpWithoutCrashFailure: |
| ++values.failed_dumps_without_crash; |
| break; |
| default: |
| // Presumably a pre-existing record from the previous implementation. |
| // We will delete it. |
| break; |
| } |
| } |
| } |
| |
| // Delete the registry keys we've just counted. |
| for (const auto& value_name : to_delete) |
| regkey.DeleteValue(value_name.c_str()); |
| |
| return values; |
| } |
| |
| } // namespace browser_watcher |