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