summaryrefslogtreecommitdiffstats
path: root/chrome_frame/crash_reporting/crash_report.cc
blob: 32c81318c24fd4fbe41407e5fb3193b18ea5887e (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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// Copyright (c) 2011 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.

// crash_report.cc : Implementation crash reporting.
#include "chrome_frame/crash_reporting/crash_report.h"

#include "base/basictypes.h"
#include "base/synchronization/lock.h"
#include "breakpad/src/client/windows/handler/exception_handler.h"
#include "chrome_frame/crash_reporting/crash_metrics.h"

// TODO(joshia): factor out common code with chrome used for crash reporting
const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";

// This lock protects against concurrent access to g_breakpad.
static base::Lock g_breakpad_lock;
static google_breakpad::ExceptionHandler* g_breakpad = NULL;

// These minidump flag combinations have been tested safe against the
// DbgHelp.dll version that ships with Windows XP SP2.
const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>(
    MiniDumpWithProcessThreadData |  // Get PEB and TEB.
    MiniDumpWithUnloadedModules);  // Get unloaded modules when available.

const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
    MiniDumpWithProcessThreadData |  // Get PEB and TEB.
    MiniDumpWithUnloadedModules |  // Get unloaded modules when available.
    MiniDumpWithIndirectlyReferencedMemory);  // Get memory referenced by stack.

// Large dump with all process memory.
const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>(
    MiniDumpWithFullMemory |  // Full memory from process.
    MiniDumpWithProcessThreadData |  // Get PEB and TEB.
    MiniDumpWithHandleData |  // Get all handle information.
    MiniDumpWithUnloadedModules);  // Get unloaded modules when available.

#pragma code_seg(push, ".text$va")
static void veh_segment_start() {}
#pragma code_seg(pop)

#pragma code_seg(push, ".text$vz")
static void veh_segment_end() {}
#pragma code_seg(pop)

// Place code in .text$veh_m.
#pragma code_seg(push, ".text$vm")
#include "chrome_frame/crash_reporting/vectored_handler-impl.h"

class CrashHandler {
 public:
  CrashHandler() : veh_id_(NULL), handler_(&crash_api_) {}

  // Note that breakpad_lock is used to protect accesses to breakpad and must
  // be held when Init() is called.
  bool Init(google_breakpad::ExceptionHandler* breakpad,
            base::Lock* breakpad_lock);

  void Shutdown();
 private:
  VectoredHandlerT<CrashHandlerTraits> handler_;
  CrashHandlerTraits crash_api_;
  void* veh_id_;

  static LONG WINAPI VectoredHandlerEntryPoint(EXCEPTION_POINTERS* exptrs);
};

static CrashHandler g_crash_handler;

// Turn off FPO optimization, so ::RtlCaptureStackBackTrace returns sane result.
#pragma optimize("y", off)
LONG WINAPI CrashHandler::VectoredHandlerEntryPoint(
    EXCEPTION_POINTERS* exptrs) {
  return g_crash_handler.handler_.Handler(exptrs);
}
#pragma optimize("y", on)

#pragma code_seg(pop)

bool CrashHandler::Init(google_breakpad::ExceptionHandler* breakpad,
                        base::Lock* breakpad_lock) {
  DCHECK(breakpad);
  DCHECK(breakpad_lock);
  breakpad_lock->AssertAcquired();

  if (veh_id_)
    return true;

  crash_api_.Init(&veh_segment_start, &veh_segment_end,
                  &WriteMinidumpForException);

  void* id = ::AddVectoredExceptionHandler(FALSE, &VectoredHandlerEntryPoint);
  if (id != NULL) {
    veh_id_ = id;
    return true;
  } else {
    crash_api_.Shutdown();
    return false;
  }
}

void CrashHandler::Shutdown() {
  if (veh_id_) {
    ::RemoveVectoredExceptionHandler(veh_id_);
    veh_id_ = NULL;
  }

  crash_api_.Shutdown();
}

std::wstring GetCrashServerPipeName(const std::wstring& user_sid) {
  std::wstring pipe_name = kGoogleUpdatePipeName;
  pipe_name += user_sid;
  return pipe_name;
}

bool InitializeVectoredCrashReportingWithPipeName(
    bool full_dump,
    const wchar_t* pipe_name,
    const std::wstring& dump_path,
    google_breakpad::CustomClientInfo* client_info) {
  base::AutoLock lock(g_breakpad_lock);
  if (g_breakpad)
    return true;

  if (dump_path.empty()) {
    return false;
  }

  // TODO(siggi): Consider switching to kSmallerDumpType post-beta.
  MINIDUMP_TYPE dump_type = full_dump ? kFullDumpType : kLargerDumpType;
  g_breakpad = new google_breakpad::ExceptionHandler(
      dump_path, NULL, NULL, NULL,
      google_breakpad::ExceptionHandler::HANDLER_INVALID_PARAMETER |
      google_breakpad::ExceptionHandler::HANDLER_PURECALL, dump_type,
      pipe_name, client_info);

  if (!g_breakpad)
    return false;

  if (!g_crash_handler.Init(g_breakpad, &g_breakpad_lock)) {
    delete g_breakpad;
    g_breakpad = NULL;
    return false;
  }

  return true;
}

bool InitializeVectoredCrashReporting(
    bool full_dump,
    const wchar_t* user_sid,
    const std::wstring& dump_path,
    google_breakpad::CustomClientInfo* client_info) {
  DCHECK(user_sid);
  DCHECK(client_info);

  std::wstring pipe_name = GetCrashServerPipeName(user_sid);

  return InitializeVectoredCrashReportingWithPipeName(full_dump,
                                                      pipe_name.c_str(),
                                                      dump_path,
                                                      client_info);
}

bool ShutdownVectoredCrashReporting() {
  g_crash_handler.Shutdown();
  base::AutoLock lock(g_breakpad_lock);
  delete g_breakpad;
  g_breakpad = NULL;
  return true;
}

bool WriteMinidumpForException(EXCEPTION_POINTERS* p) {
  base::AutoLock lock(g_breakpad_lock);
  CrashMetricsReporter::GetInstance()->IncrementMetric(
      CrashMetricsReporter::CRASH_COUNT);
  bool success = false;
  if (g_breakpad) {
    success = g_breakpad->WriteMinidumpForException(p);
  }
  return success;
}