| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 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/browser_watcher/crash_reporting_metrics_win.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include <algorithm> | |
| 10 #include "base/atomicops.h" | |
| 11 #include "base/guid.h" | |
| 12 #include "base/logging.h" | |
| 13 #include "base/macros.h" | |
| 14 #include "base/strings/safe_sprintf.h" | |
| 15 #include "base/strings/string_util.h" | |
| 16 #include "base/strings/utf_string_conversions.h" | |
| 17 #include "base/win/registry.h" | |
| 18 | |
| 19 namespace browser_watcher { | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 // The prior implementation used 0 and 1. Its data collection is inconsistent | |
| 24 // with ours and it's best to just ignore its records. So we start at 2. | |
| 25 const DWORD kCrashDumpAttempt = 2; | |
| 26 const DWORD kDumpWithoutCrashAttempt = 3; | |
| 27 const DWORD kCrashDumpSuccess = 4; | |
| 28 const DWORD kCrashDumpFailure = 5; | |
| 29 const DWORD kDumpWithoutCrashSuccess = 6; | |
| 30 const DWORD kDumpWithoutCrashFailure = 7; | |
| 31 | |
| 32 // A prefix intended to avoid registry value name collisions with other | |
| 33 // processes (or modules within a single process). It is not necessary for the | |
| 34 // reading and writing instances to share the prefix value. This array is sized | |
| 35 // to hold a null-terminated string as generated by base::GenerateGUID. | |
| 36 char g_unique_prefix[36 + 1] = {0}; | |
| 37 | |
| 38 // An atomic counter intended to make each registry value name unique within | |
| 39 // this process and module. | |
| 40 base::subtle::Atomic32 g_record_count = 0; | |
| 41 | |
| 42 // The length of a null-terminated string consisting of "{GUID}-{COUNT}". | |
| 43 const size_t kValueNameSize = 36 + 1 + 8 + 1; | |
| 44 | |
| 45 void WriteValue(const base::string16& key_path, DWORD value) { | |
| 46 // Generate the final value name we'll use (appends the crash number to the | |
| 47 // base value name). | |
| 48 char value_name_ascii[kValueNameSize] = ""; | |
| 49 base::char16 value_name_utf16[kValueNameSize] = L""; | |
| 50 | |
| 51 if (base::strings::SafeSPrintf( | |
| 52 value_name_ascii, "%s-%x", g_unique_prefix, | |
| 53 base::subtle::NoBarrier_AtomicIncrement(&g_record_count, 1)) <= 0) { | |
| 54 NOTREACHED(); | |
| 55 } else { | |
| 56 // Since it's an ASCII string, the UTF-16 form is identical with leading 0 | |
| 57 // bytes. We're avoiding unnecessary heap operations since we're running in | |
| 58 // a compromised process. | |
| 59 std::copy(value_name_ascii, value_name_ascii + kValueNameSize, | |
| 60 value_name_utf16); | |
| 61 | |
| 62 base::win::RegKey reg_key; | |
| 63 if (reg_key.Create(HKEY_CURRENT_USER, key_path.c_str(), KEY_SET_VALUE) != | |
| 64 ERROR_SUCCESS) { | |
| 65 NOTREACHED(); | |
| 66 } else { | |
| 67 reg_key.WriteValue(value_name_utf16, value); | |
| 68 } | |
| 69 } | |
| 70 | |
| 71 } | |
| 72 | |
| 73 } // namespace | |
| 74 | |
| 75 CrashReportingMetrics::CrashReportingMetrics( | |
| 76 const base::string16& registry_path) | |
| 77 : registry_path_(registry_path) { | |
| 78 if (g_unique_prefix[0] == 0) { | |
| 79 std::string guid = base::GenerateGUID(); | |
| 80 // It seems reasonable to assume that the worst possible outcome of two | |
| 81 // separate threads trying to do the following would be to store a GUID | |
| 82 // value that is a hybrid of the two intended values. Hence we can avoid any | |
| 83 // thread-safety caveats in our public API. | |
| 84 size_t copied_size = base::strlcpy(g_unique_prefix, guid.c_str(), | |
| 85 arraysize(g_unique_prefix)); | |
| 86 DCHECK_EQ(copied_size, guid.length()); | |
| 87 } | |
| 88 } | |
| 89 | |
| 90 void CrashReportingMetrics::RecordCrashDumpAttempt() { | |
| 91 WriteValue(registry_path_, kCrashDumpAttempt); | |
| 92 } | |
| 93 | |
| 94 void CrashReportingMetrics::RecordDumpWithoutCrashAttempt() { | |
| 95 WriteValue(registry_path_, kDumpWithoutCrashAttempt); | |
| 96 } | |
| 97 void CrashReportingMetrics::RecordCrashDumpAttemptResult(bool succeeded) { | |
| 98 WriteValue(registry_path_, succeeded ? kCrashDumpSuccess : kCrashDumpFailure); | |
| 99 } | |
| 100 void CrashReportingMetrics::RecordDumpWithoutCrashAttemptResult( | |
| 101 bool succeeded) { | |
| 102 WriteValue(registry_path_, | |
| 103 succeeded ? kDumpWithoutCrashSuccess : kDumpWithoutCrashFailure); | |
| 104 } | |
| 105 | |
| 106 CrashReportingMetrics::Values CrashReportingMetrics::RetrieveAndResetMetrics() { | |
| 107 Values values = {0}; | |
| 108 | |
| 109 // Open the registry key for iteration. | |
| 110 base::win::RegKey regkey; | |
| 111 if (regkey.Open(HKEY_CURRENT_USER, registry_path_.c_str(), | |
| 112 KEY_QUERY_VALUE | KEY_SET_VALUE) != ERROR_SUCCESS) { | |
| 113 return values; | |
| 114 } | |
| 115 | |
| 116 // Track a list of values to delete. We don't modify the registry key while | |
| 117 // we're iterating over its values. | |
| 118 typedef std::vector<base::string16> StringVector; | |
| 119 StringVector to_delete; | |
| 120 | |
| 121 // Iterate over the values in the key counting dumps with and without crashes. | |
| 122 // We directly walk the values instead of using RegistryValueIterator in order | |
| 123 // to read all of the values as DWORDS instead of strings. | |
| 124 base::string16 name; | |
| 125 DWORD value = 0; | |
| 126 for (int i = regkey.GetValueCount() - 1; i >= 0; --i) { | |
| 127 if (regkey.GetValueNameAt(i, &name) == ERROR_SUCCESS && | |
| 128 regkey.ReadValueDW(name.c_str(), &value) == ERROR_SUCCESS) { | |
| 129 to_delete.push_back(name); | |
| 130 switch (value) { | |
| 131 case kCrashDumpAttempt: | |
| 132 ++values.crash_dump_attempts; | |
| 133 break; | |
| 134 case kCrashDumpSuccess: | |
| 135 ++values.successful_crash_dumps; | |
| 136 break; | |
| 137 case kCrashDumpFailure: | |
| 138 ++values.failed_crash_dumps; | |
| 139 break; | |
| 140 case kDumpWithoutCrashAttempt: | |
| 141 ++values.dump_without_crash_attempts; | |
| 142 break; | |
| 143 case kDumpWithoutCrashSuccess: | |
| 144 ++values.successful_dumps_without_crash; | |
| 145 break; | |
| 146 case kDumpWithoutCrashFailure: | |
| 147 ++values.failed_dumps_without_crash; | |
| 148 break; | |
| 149 default: | |
| 150 // Presumably a pre-existing record from the previous implementation. | |
| 151 // We will delete it. | |
| 152 break; | |
| 153 } | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 // Delete the registry keys we've just counted. | |
| 158 for (const auto& value_name : to_delete) | |
| 159 regkey.DeleteValue(value_name.c_str()); | |
| 160 | |
| 161 return values; | |
| 162 } | |
| 163 | |
| 164 } // namespace browser_watcher | |
| OLD | NEW |