| // 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 |