summaryrefslogtreecommitdiffstats
path: root/components/browser_watcher/crash_reporting_metrics_win.cc
blob: ee83c4ccf92e44e10ba587b6d2beb40ab1b50b22 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// 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