summaryrefslogtreecommitdiffstats
path: root/o3d/breakpad/win/exception_handler_win32.cc
blob: 70a2607f96b791d02beb60950f6f0db527600be3 (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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/*
 * Copyright 2009, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

//
// Wrapper class for using the Breakpad crash reporting system.
// (adapted from code in Google Gears)
//

#include <windows.h>
#include <assert.h>
#include <shellapi.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <tchar.h>
#include <time.h>
#include <string>

#include "breakpad/win/breakpad_config.h"
#include "breakpad/win/exception_handler_win32.h"
#include "client/windows/handler/exception_handler.h"

// We use g_logger to determine if we opt-in/out of crash reporting
namespace o3d {
  class PluginLogging;
}
extern o3d::PluginLogging* g_logger;

// Some simple string typedefs
#define STRING16(x) reinterpret_cast<const char16*>(x)

typedef wchar_t char16;

namespace std {
typedef wstring string16;
}


ExceptionManager* ExceptionManager::instance_ = NULL;


ExceptionManager::ExceptionManager(bool catch_entire_process)
    : catch_entire_process_(catch_entire_process),
      exception_handler_(NULL) {
  assert(!instance_);
  instance_ = this;
}


ExceptionManager::~ExceptionManager() {
  if (exception_handler_)
    delete exception_handler_;
  assert(instance_ == this);
  instance_ = NULL;
}


static HMODULE GetModuleHandleFromAddress(void *address) {
  MEMORY_BASIC_INFORMATION mbi;
  SIZE_T result = VirtualQuery(address, &mbi, sizeof(mbi));
  return static_cast<HMODULE>(mbi.AllocationBase);
}


// Gets the handle to the currently executing module.
static HMODULE GetCurrentModuleHandle() {
  // pass a pointer to the current function
  return GetModuleHandleFromAddress(GetCurrentModuleHandle);
}


static bool IsAddressInCurrentModule(void *address) {
  return GetCurrentModuleHandle() == GetModuleHandleFromAddress(address);
}


// Called back when an exception occurs - we can decide here if we
// want to handle this crash...
//
static bool FilterCallback(void *context,
                           EXCEPTION_POINTERS *exinfo,
                           MDRawAssertionInfo *assertion) {
  // g_logger will be NULL if user opts out of metrics/crash reporting
  if (!g_logger) return false;

  ExceptionManager* this_ptr = reinterpret_cast<ExceptionManager*>(context);

  if (this_ptr->catch_entire_process())
    return true;

  if (!exinfo)
    return true;

  return IsAddressInCurrentModule(exinfo->ExceptionRecord->ExceptionAddress);
}


// Is called by Breakpad when an exception occurs and a minidump has been
// written to disk.
static bool MinidumpCallback(const wchar_t *minidump_folder,
                             const wchar_t *minidump_id,
                             void *context,
                             EXCEPTION_POINTERS *exinfo,
                             MDRawAssertionInfo *assertion,
                             bool succeeded) {
  // If this is set to |false| then the exception will be forwarded to
  // Windows and a crash dialog will appear
  bool handled_exception = true;

  // get the full path to the minidump
  wchar_t minidump_path[MAX_PATH];
  _snwprintf(minidump_path, sizeof(minidump_path), L"%s\\%s.dmp",
             minidump_folder, minidump_id);



  // determine the full path to "reporter.exe"
  // it will look something like:
  // "c:\Documents and Settings\user\Application Data\Google\O3D\reporter.exe"
  TCHAR reporterPath[MAX_PATH];

  HRESULT result = SHGetFolderPath(
      NULL,
      O3D_PLUGIN_INSTALLDIR_CSIDL,
      NULL,
      0,
      reporterPath);

  if (result == 0) {
    PathAppend(reporterPath,
      _T(O3D_PLUGIN_VENDOR_DIRECTORY) _T("\\")
      _T(O3D_PLUGIN_PRODUCT_DIRECTORY) _T("\\reporter.exe"));
  }

  if (PathFileExists(reporterPath)) {
    std::string16 command_line;
    command_line += L"\"";
    command_line += reporterPath;
    command_line += L"\" \"";
    command_line += minidump_path;
    command_line += L"\" \"";
    command_line += kCrashReportProductName;
    command_line += L"\" \"";
    command_line += kCrashReportProductVersion;
    command_line += L"\"";

    // execute the process
    STARTUPINFO startup_info = {0};
    startup_info.cb = sizeof(startup_info);
    PROCESS_INFORMATION process_info = {0};
    CreateProcessW(NULL,   // application name (NULL to get from command line)
                   const_cast<char16 *>(command_line.c_str()),
                   NULL,   // process attributes (NULL means process handle not
                           // inheritable)
                   NULL,   // thread attributes (NULL means thread handle not
                           // inheritable)
                   FALSE,  // inherit handles
                   0,      // creation flags
                   NULL,   // environment block (NULL to use parent's)
                   NULL,   // starting block (NULL to use parent's)
                   &startup_info,
                   &process_info);
    CloseHandle(process_info.hProcess);
    CloseHandle(process_info.hThread);
  } else {
    handled_exception = false;
  }

  return handled_exception;
}


void ExceptionManager::StartMonitoring() {
#ifdef O3D_ENABLE_BREAKPAD
  if (exception_handler_) { return; }  // don't init more than once

  wchar_t temp_path[MAX_PATH];
  if (!GetTempPathW(MAX_PATH, temp_path)) { return; }

  exception_handler_ = new google_breakpad::ExceptionHandler(temp_path,
                                                             FilterCallback,
                                                             MinidumpCallback,
                                                             this, true);
#endif
}