summaryrefslogtreecommitdiffstats
path: root/chrome/app/kasko_client.cc
blob: f9699e9073cc0ae3631eb3db4bf1d116b2e177e9 (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
// 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.

#if defined(KASKO)

#include "chrome/app/kasko_client.h"

#include <windows.h>

#include <string>
#include <vector>

#include "base/logging.h"
#include "base/process/process_handle.h"
#include "breakpad/src/client/windows/common/ipc_protocol.h"
#include "chrome/app/chrome_watcher_client_win.h"
#include "chrome/chrome_watcher/chrome_watcher_main_api.h"
#include "chrome/common/chrome_constants.h"
#include "components/crash/content/app/crash_keys_win.h"
#include "syzygy/kasko/api/client.h"

namespace {

ChromeWatcherClient* g_chrome_watcher_client = nullptr;
kasko::api::MinidumpType g_minidump_type = kasko::api::SMALL_DUMP_TYPE;

void GetKaskoCrashKeys(const kasko::api::CrashKey** crash_keys,
                       size_t* crash_key_count) {
  static_assert(
      sizeof(kasko::api::CrashKey) == sizeof(google_breakpad::CustomInfoEntry),
      "CrashKey and CustomInfoEntry structs are not compatible.");
  static_assert(offsetof(kasko::api::CrashKey, name) ==
                    offsetof(google_breakpad::CustomInfoEntry, name),
                "CrashKey and CustomInfoEntry structs are not compatible.");
  static_assert(offsetof(kasko::api::CrashKey, value) ==
                    offsetof(google_breakpad::CustomInfoEntry, value),
                "CrashKey and CustomInfoEntry structs are not compatible.");
  static_assert(
      sizeof(reinterpret_cast<kasko::api::CrashKey*>(0)->name) ==
          sizeof(reinterpret_cast<google_breakpad::CustomInfoEntry*>(0)->name),
      "CrashKey and CustomInfoEntry structs are not compatible.");
  static_assert(
      sizeof(reinterpret_cast<kasko::api::CrashKey*>(0)->value) ==
          sizeof(reinterpret_cast<google_breakpad::CustomInfoEntry*>(0)->value),
      "CrashKey and CustomInfoEntry structs are not compatible.");

  *crash_key_count =
      breakpad::CrashKeysWin::keeper()->custom_info_entries().size();
  *crash_keys = reinterpret_cast<const kasko::api::CrashKey*>(
      breakpad::CrashKeysWin::keeper()->custom_info_entries().data());
}

}  // namespace

KaskoClient::KaskoClient(ChromeWatcherClient* chrome_watcher_client,
                         kasko::api::MinidumpType minidump_type) {
  DCHECK(!g_chrome_watcher_client);
  g_minidump_type = minidump_type;
  g_chrome_watcher_client = chrome_watcher_client;

  kasko::api::InitializeClient(
      GetKaskoEndpoint(base::GetCurrentProcId()).c_str());

  // Register the crash keys so that they will be available whether a crash
  // report is triggered directly by the browser process or requested by the
  // Chrome Watcher process.
  size_t crash_key_count = 0;
  const kasko::api::CrashKey* crash_keys = nullptr;
  GetKaskoCrashKeys(&crash_keys, &crash_key_count);
  kasko::api::RegisterCrashKeys(crash_keys, crash_key_count);
}

KaskoClient::~KaskoClient() {
  DCHECK(g_chrome_watcher_client);
  g_chrome_watcher_client = nullptr;
  kasko::api::ShutdownClient();
}

// Sends a diagnostic report for the current process, then terminates it.
// |info| is an optional exception record describing an exception on the current
// thread.
// |protobuf| is an optional buffer of length |protobuf_length|.
// |base_addresses| and |lengths| are optional null-terminated arrays of the
// same length. For each entry in |base_addresses|, a memory range starting at
// the specified address and having the length specified in the corresponding
// entry in |lengths| will be explicitly included in the report.
extern "C" void __declspec(dllexport)
    ReportCrashWithProtobufAndMemoryRanges(EXCEPTION_POINTERS* info,
                                           const char* protobuf,
                                           size_t protobuf_length,
                                           const void* const* base_addresses,
                                           const size_t* lengths) {
  if (g_chrome_watcher_client && g_chrome_watcher_client->EnsureInitialized()) {
    size_t crash_key_count = 0;
    const kasko::api::CrashKey* crash_keys = nullptr;
    GetKaskoCrashKeys(&crash_keys, &crash_key_count);
    std::vector<kasko::api::MemoryRange> memory_ranges;
    if (base_addresses && lengths) {
      for (int i = 0; base_addresses[i] != nullptr && lengths[i] != 0; ++i) {
        kasko::api::MemoryRange memory_range = {base_addresses[i], lengths[i]};
        memory_ranges.push_back(memory_range);
      }
    }
    kasko::api::SendReport(info, g_minidump_type, protobuf, protobuf_length,
                           crash_keys, crash_key_count,
                           memory_ranges.size() ? &memory_ranges[0] : nullptr,
                           memory_ranges.size());
  }

  // The Breakpad integration hooks TerminateProcess. Sidestep it to avoid a
  // secondary report.

  using TerminateProcessWithoutDumpProc = void(__cdecl*)();
  TerminateProcessWithoutDumpProc terminate_process_without_dump =
      reinterpret_cast<TerminateProcessWithoutDumpProc>(::GetProcAddress(
          ::GetModuleHandle(chrome::kBrowserProcessExecutableName),
          "TerminateProcessWithoutDump"));
  CHECK(terminate_process_without_dump);
  terminate_process_without_dump();
}

extern "C" void __declspec(dllexport) ReportCrashWithProtobuf(
    EXCEPTION_POINTERS* info, const char* protobuf, size_t protobuf_length) {
  ReportCrashWithProtobufAndMemoryRanges(info, protobuf, protobuf_length,
                                         nullptr, nullptr);
}

#endif  // defined(KASKO)