summaryrefslogtreecommitdiffstats
path: root/o3d/breakpad
diff options
context:
space:
mode:
Diffstat (limited to 'o3d/breakpad')
-rw-r--r--o3d/breakpad/build.scons86
-rw-r--r--o3d/breakpad/win/bluescreen_detector.cc331
-rw-r--r--o3d/breakpad/win/bluescreen_detector.h219
-rw-r--r--o3d/breakpad/win/bluescreen_detector_test.cc322
-rw-r--r--o3d/breakpad/win/breakpad_config.cc74
-rw-r--r--o3d/breakpad/win/breakpad_config.h57
-rw-r--r--o3d/breakpad/win/crash_sender_win32.cc153
-rw-r--r--o3d/breakpad/win/exception_handler_win32.cc210
-rw-r--r--o3d/breakpad/win/exception_handler_win32.h76
9 files changed, 1528 insertions, 0 deletions
diff --git a/o3d/breakpad/build.scons b/o3d/breakpad/build.scons
new file mode 100644
index 0000000..235bfdc
--- /dev/null
+++ b/o3d/breakpad/build.scons
@@ -0,0 +1,86 @@
+# 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.
+
+
+# Import the build environment that was decided in the project SConstruct.
+# This may be the Debug or Optimized environment
+Import('env')
+
+env.Append(
+ CPPPATH = [
+ '$BREAKPAD_DIR',
+ ],
+)
+# Create a list of the files used to build the target library.
+library_inputs = [
+ 'win/exception_handler_win32.cc',
+ 'win/breakpad_config.cc',
+ 'win/bluescreen_detector.cc',
+]
+
+library_breakpad_inputs = [
+ 'client/windows/handler/exception_handler',
+ 'client/windows/crash_generation/crash_generation_client',
+ 'common/windows/guid_string',
+ 'common/windows/http_upload',
+ 'common/windows/string_utils',
+]
+library_breakpad_objects = env.MakeObjects(library_breakpad_inputs,
+ '$BREAKPAD_DIR',
+ 'cc')
+
+# Create a target library from the sources called 'o3dBreakpad'
+env.ComponentLibrary('o3dBreakpad', library_inputs + library_breakpad_objects)
+
+# Create a list of the files used to build 'reporter.exe'.
+reporter_inputs = [
+ 'win/crash_sender_win32.cc',
+]
+reporter_breakpad_inputs = [
+ 'client/windows/sender/crash_report_sender',
+]
+reporter_breakpad_objects = env.MakeObjects(reporter_breakpad_inputs,
+ '$BREAKPAD_DIR',
+ 'cc')
+
+env.Append(
+ LIBS = [
+ 'AdvAPI32',
+ 'WinInet',
+ 'shlwapi',
+ 'o3dBreakpad',
+ ],
+)
+
+# Create the breakpad 'reporter.exe' tool for uploading crash reports
+reporter = env.ComponentProgram('reporter',
+ reporter_inputs + reporter_breakpad_objects)
+
+# Copy the resulting executable to the Artifacts directory.
+env.Replicate('$ARTIFACTS_DIR', reporter)
diff --git a/o3d/breakpad/win/bluescreen_detector.cc b/o3d/breakpad/win/bluescreen_detector.cc
new file mode 100644
index 0000000..9cdd51f
--- /dev/null
+++ b/o3d/breakpad/win/bluescreen_detector.cc
@@ -0,0 +1,331 @@
+/*
+ * 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.
+ */
+
+
+// BluescreenDetector attempts to identify cases where the machine bluescreened
+// and was caused by o3d.
+
+#include "breakpad/win/bluescreen_detector.h"
+#include "plugin/cross/plugin_logging.h"
+#include "plugin/cross/plugin_metrics.h"
+
+#ifdef OS_WIN
+#include <windows.h>
+#include <rpc.h>
+#endif
+
+extern o3d::PluginLogging* g_logger;
+
+namespace o3d {
+
+using std::vector;
+
+#ifdef OS_WIN
+
+const wchar_t *kMarkerFileSuffix = L"_bs";
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+BluescreenDetector::BluescreenDetector()
+ : started_(false) {
+ time_manager_ = new TimeManager();
+ marker_file_manager_ = new MarkerFileManager(time_manager_);
+ bluescreen_logger_ = new BluescreenLogger();
+}
+#endif // OS_WIN
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+BluescreenDetector::~BluescreenDetector() {
+ if (started_) {
+ Stop();
+ }
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void BluescreenDetector::Start() {
+ // Here we check if any marker files (from a previous session) were not
+ // properly cleaned up. If so, then a blue-screen may have been caused
+ // by us and we'll log it.
+ int num_bluescreens = marker_file_manager_->DetectStrayMarkerFiles();
+
+ if (num_bluescreens > 0) {
+ bluescreen_logger_->LogBluescreen(num_bluescreens);
+ }
+
+ // Create a marker file for this session - it will be removed when the plugin
+ // unloads (or in the breakpad exception handler). If a blue-screen happens,
+ // then this file will not be deleted and we'll hopefully detect it the next
+ // time around (in a call to DetectStrayMarkerFiles())
+ marker_file_manager_->CreateMarkerFile();
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void BluescreenDetector::Stop() {
+ marker_file_manager_->RemoveMarkerFile();
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// MarkerFileManager
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Base implementation shared by mock and actual sub-classes
+int MarkerFileManagerInterface::DetectStrayMarkerFiles() {
+ vector<MarkerFileInfo> marker_files;
+ GetMarkerFileList(&marker_files);
+
+ // Go through all marker files and look for ones which were created before
+ // the machine was last booted
+ int stray_file_count = 0;
+
+ for (int i = 0; i < marker_files.size(); ++i) {
+ const MarkerFileInfo &file_info = marker_files[i];
+
+ if (time_manager_->IsMarkerFileOld(file_info)) {
+ // We've found a marker file which was created before we last re-booted
+ // This could signal a blue-screen which we caused
+
+ // Clean it up, so we don't continue detecting it again and logging a
+ // blue-screen more than once
+ DeleteMarkerFile(file_info);
+ ++stray_file_count;
+ // don't break here, since we need to detect and delete all old ones
+ }
+ }
+
+ return stray_file_count;
+}
+
+#ifdef OS_WIN
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void MarkerFileManager::CreateMarkerFile() {
+ if (marker_file_) {
+ // error
+ return;
+ }
+
+ std::wstring marker_dir = GetMarkerDirectory();
+ std::wstring uuid_string = GetUUIDString();
+
+ // format a complete file path for the marker file
+ wchar_t fullpath[MAX_PATH];
+ _snwprintf(fullpath, MAX_PATH, L"%s%s%s",
+ marker_dir.c_str(),
+ uuid_string.c_str(),
+ kMarkerFileSuffix);
+
+ marker_file_name_ = fullpath;
+
+ // Note that the file is created with the attribute FILE_FLAG_DELETE_ON_CLOSE
+ // so even if the process crashes, this file will get cleaned up.
+ // It's only if a bluescreen occurs (or plug is pulled out of wall) that the
+ // file will not be deleted (in theory)
+ marker_file_ = CreateFileW(fullpath,
+ GENERIC_WRITE,
+ 0,
+ NULL,
+ CREATE_NEW,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
+ NULL);
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void MarkerFileManager::RemoveMarkerFile() {
+ if (marker_file_) {
+ // Strictly speaking, we don't really need to delete the file here since
+ // the system will do it for us since FILE_FLAG_DELETE_ON_CLOSE was used,
+ // but let's do it just to be sure...
+ CloseHandle(marker_file_);
+ marker_file_ = NULL;
+ DeleteFile(marker_file_name_.c_str());
+ }
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static MarkerFileInfo GetMarkerFileInfo(const WIN32_FIND_DATA &find_data,
+ const std::wstring &marker_dir) {
+ std::wstring file_name = find_data.cFileName;
+ std::wstring full_pathname_w = marker_dir + file_name;
+ String full_pathname = WideToUTF8(full_pathname_w);
+ uint64 creation_time =
+ TimeManager::FileTimeToUInt64(find_data.ftCreationTime);
+
+ MarkerFileInfo file_info(full_pathname, creation_time);
+ return file_info;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool MarkerFileManager::GetMarkerFileList(vector<MarkerFileInfo> *file_list) {
+ // Search the marker directory for all files ending in kMarkerFileSuffix
+ std::wstring marker_dir = GetMarkerDirectory();
+ std::wstring search_string = marker_dir + L"*" + kMarkerFileSuffix;
+
+ WIN32_FIND_DATA find_data;
+ HANDLE h = ::FindFirstFile(search_string.c_str(), &find_data);
+
+ if (h != INVALID_HANDLE_VALUE) {
+ MarkerFileInfo file_info = GetMarkerFileInfo(find_data, marker_dir);
+ file_list->push_back(file_info);
+
+ BOOL file_valid = true;
+
+ while (file_valid) {
+ file_valid = ::FindNextFile(h, &find_data);
+ if (file_valid) {
+ MarkerFileInfo file_info = GetMarkerFileInfo(find_data, marker_dir);
+ file_list->push_back(file_info);
+ }
+ }
+
+ ::FindClose(h);
+ } else {
+ // This can happen if there are no marker files found
+ return true;
+ }
+
+ return true;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void MarkerFileManager::DeleteMarkerFile(const MarkerFileInfo &file_info) {
+ std::wstring filename_w = UTF8ToWide(file_info.GetName());
+ ::DeleteFileW(filename_w.c_str());
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const std::wstring MarkerFileManager::GetUUIDString() {
+ // now generate a GUID
+ UUID guid = {0};
+ ::UuidCreate(&guid);
+
+ // and format into a wide-string
+ wchar_t guid_string[37];
+ _snwprintf(
+ guid_string, sizeof(guid_string) / sizeof(guid_string[0]),
+ L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ guid.Data1, guid.Data2, guid.Data3,
+ guid.Data4[0], guid.Data4[1], guid.Data4[2],
+ guid.Data4[3], guid.Data4[4], guid.Data4[5],
+ guid.Data4[6], guid.Data4[7]);
+
+ return guid_string;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const std::wstring MarkerFileManager::GetMarkerDirectory() {
+ wchar_t temp_path[MAX_PATH];
+ if (!::GetTempPathW(MAX_PATH, temp_path)) {
+ return L"c:\\windows\\temp\\";
+ }
+ return temp_path;
+}
+
+#endif // OS_WIN
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// TimeManager
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool TimeManagerInterface::IsMarkerFileOld(const MarkerFileInfo &file_info) {
+ uint64 creation_time = file_info.GetCreationTime();
+ uint64 current_time = GetCurrentTime();
+
+ if (current_time < creation_time) {
+ // should never happen, but log error
+ return false; // something wrong has happened here
+ }
+
+ uint64 file_time_since_now = current_time - creation_time;
+ uint64 up_time = GetUpTime();
+
+ bool is_old = (file_time_since_now > up_time);
+ return is_old;
+}
+
+#ifdef OS_WIN
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+uint64 TimeManager::FileTimeToUInt64(FILETIME time) {
+ // FILETIME units are 100-nanosecond intervals
+ ULARGE_INTEGER li;
+ li.LowPart = time.dwLowDateTime;
+ li.HighPart = time.dwHighDateTime;
+ return li.QuadPart;
+}
+
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+uint64 TimeManager::GetCurrentTime() {
+ SYSTEMTIME current_time;
+ ::GetSystemTime(&current_time);
+
+ FILETIME file_time;
+ ::SystemTimeToFileTime(&current_time, &file_time);
+
+ // Now convert to uint64...
+ return FileTimeToUInt64(file_time);
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// in units of 100-nanosecond intervals (FILETIME units)
+uint64 TimeManager::GetUpTime() {
+ // NOTE : It would have been easier to simply use GetTickCount(),
+ // but it wraps around to zero after 49.7 days! There is a GetTickCount64()
+ // function but it's only available on Vista.
+ // Using QueryPerformanceCounter() and QueryPerformanceFrequency() appears
+ // to be the best alternative.
+
+ uint64 tick_count64 = 0;
+ LARGE_INTEGER count;
+ LARGE_INTEGER frequency;
+ if (QueryPerformanceCounter(&count) &&
+ QueryPerformanceFrequency(&frequency)) {
+ LONGLONG total_seconds = count.QuadPart / frequency.QuadPart;
+
+ // convert to 100-nanosecond intervals
+ const LONGLONG k100NanosecondIntervalsPerSecond = 1000*1000*10;
+ tick_count64 = total_seconds * k100NanosecondIntervalsPerSecond;
+ }
+
+ return tick_count64;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// BluescreenLogger
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void BluescreenLogger::LogBluescreen(int num_bluescreens) {
+ metric_bluescreens_total += num_bluescreens;
+ // Make sure we write this out to the registry immediately in case we're
+ // about to bluescreen again before the metrics timer fires!
+ if (g_logger) g_logger->ProcessMetrics(false, true);
+}
+#endif // OS_WIN
+
+} // namespace o3d
diff --git a/o3d/breakpad/win/bluescreen_detector.h b/o3d/breakpad/win/bluescreen_detector.h
new file mode 100644
index 0000000..16f47a6
--- /dev/null
+++ b/o3d/breakpad/win/bluescreen_detector.h
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+
+
+// BluescreenDetector attempts to identify cases where the machine bluescreened
+// and was caused by o3d.
+
+#ifndef CLIENT3D_BREAKPAD_WIN_BLUESCREENDETECTOR_H_
+#define CLIENT3D_BREAKPAD_WIN_BLUESCREENDETECTOR_H_
+
+#include <string.h>
+#include <stdlib.h>
+#include <vector>
+
+#ifdef OS_WIN
+#include <windows.h>
+#endif
+
+#include "base/basictypes.h"
+#include "core/cross/types.h"
+
+namespace o3d {
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class MarkerFileInfo {
+ public:
+ MarkerFileInfo(const String &name, uint64 creation_time)
+ : name_(name), creation_time_(creation_time) { };
+
+ MarkerFileInfo() : name_(""), creation_time_(0) { };
+
+ // explicit copy constructor
+ MarkerFileInfo(const MarkerFileInfo& info)
+ : name_(info.name_),
+ creation_time_(info.creation_time_) { }
+
+ const String &GetName() const { return name_; }
+ uint64 GetCreationTime() const { return creation_time_; }
+
+ private:
+ String name_;
+ uint64 creation_time_;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// We create interfaces so that we can mock these elements in the unit tests
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// TimeManagerInterface deals with times:
+// - time since boot
+// - current time
+// - file creation time
+//
+// with this information, it helps determine if marker file is "new" or "old"
+// "old" means the file was created before the last time the machine was booted
+//
+
+class TimeManagerInterface {
+ public:
+ // times are in units of 100-nanosecond intervals (FILETIME units)
+ virtual uint64 GetCurrentTime() = 0;
+ virtual uint64 GetUpTime() = 0;
+
+ // Returns |true| if the marker file was created before the machine
+ // was last re-booted
+ bool IsMarkerFileOld(const MarkerFileInfo &file_info);
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Manages a directory where "marker" files will be written. The presence or
+// absence of a marker file, along with its creation date, current time, and
+// time since boot can be used to help determine if a blue-screen has occurred.
+class MarkerFileManagerInterface {
+ public:
+ MarkerFileManagerInterface(TimeManagerInterface *time_manager)
+ : time_manager_(time_manager) {}
+
+ virtual void CreateMarkerFile() = 0;
+ virtual void RemoveMarkerFile() = 0;
+
+
+ // By looking at the creation date, along with the current time,
+ // and the "up time" (time since boot) we can tell if any "marker" files were
+ // created before boot time. If found, they will be considered as evidence for
+ // a blue-screen event in the past. Returns the number of such files found.
+ int DetectStrayMarkerFiles();
+
+ protected:
+ virtual bool GetMarkerFileList(std::vector<MarkerFileInfo> *file_list) = 0;
+ virtual void DeleteMarkerFile(const MarkerFileInfo &file_info) = 0;
+
+ TimeManagerInterface *time_manager_;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class BluescreenLoggerInterface {
+ public:
+ virtual void LogBluescreen(int num_bluescreens) = 0;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Actual implementation
+
+// We only actually implement the interfaces for Windows, but we can unit test
+// (with mocks) on all platforms
+#ifdef OS_WIN
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class MarkerFileManager : public MarkerFileManagerInterface {
+ public:
+ MarkerFileManager(TimeManagerInterface *time_manager)
+ : MarkerFileManagerInterface(time_manager), marker_file_(NULL) { };
+
+ // "marker" file management. The marker file is used to check for future
+ // blue-screens.
+ virtual void CreateMarkerFile();
+ virtual void RemoveMarkerFile();
+
+ private:
+ virtual bool GetMarkerFileList(std::vector<MarkerFileInfo> *file_list);
+ virtual void DeleteMarkerFile(const MarkerFileInfo &file_info);
+
+ const std::wstring GetMarkerDirectory();
+ const std::wstring GetUUIDString();
+
+ HANDLE marker_file_;
+ std::wstring marker_file_name_;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class TimeManager : public TimeManagerInterface {
+ public:
+ virtual uint64 GetCurrentTime();
+ virtual uint64 GetUpTime();
+
+ static uint64 FileTimeToUInt64(FILETIME time);
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class BluescreenLogger : public BluescreenLoggerInterface {
+ public:
+ // Sends blue-screen info to Google
+ virtual void LogBluescreen(int num_bluescreens);
+};
+
+#endif // OS_WIN
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class BluescreenDetector {
+ public:
+ // Default constructor for real-world use
+ BluescreenDetector();
+
+ // For mocking/testing
+ BluescreenDetector(TimeManagerInterface *time_manager,
+ MarkerFileManagerInterface *marker_file_manager,
+ BluescreenLoggerInterface *bluescreen_logger)
+ : started_(false),
+ time_manager_(time_manager),
+ marker_file_manager_(marker_file_manager),
+ bluescreen_logger_(bluescreen_logger) { }
+
+ virtual ~BluescreenDetector();
+
+ // Call Start() to check for blue-screens which may have occured and log them
+ // if so. Also, writes out a "marker" to be used to check for future
+ // blue-screens.
+ //
+ // Should be called when the plugin first loads
+ void Start();
+
+ // Call when the plugin unloads - the marker file is deleted here
+ // on Windows it's unnecessary to call Stop() since the file will be
+ // automatically deleted by the system when the process exits (or crashes)
+ void Stop();
+
+ private:
+ bool started_;
+
+ TimeManagerInterface *time_manager_;
+ MarkerFileManagerInterface *marker_file_manager_;
+ BluescreenLoggerInterface *bluescreen_logger_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluescreenDetector);
+};
+
+} // namespace o3d
+
+#endif // CLIENT3D_BREAKPAD_WIN_BLUESCREENDETECTOR_H_
diff --git a/o3d/breakpad/win/bluescreen_detector_test.cc b/o3d/breakpad/win/bluescreen_detector_test.cc
new file mode 100644
index 0000000..ac279f2
--- /dev/null
+++ b/o3d/breakpad/win/bluescreen_detector_test.cc
@@ -0,0 +1,322 @@
+/*
+ * 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.
+ */
+
+
+// Tests functionality of the BluescreenDetector class
+
+#include "core/cross/client.h"
+#include "tests/common/win/testing_common.h"
+#include "core/cross/error.h"
+#include "breakpad/win/bluescreen_detector.h"
+#include "plugin/cross/plugin_logging.h"
+
+// This is defined in "main_win.cc" but not in unit tests
+o3d::PluginLogging *g_logger = NULL;
+
+namespace o3d {
+
+// Test fixture for BluescreenDetector testing.
+class BluescreenDetectorTest : public testing::Test {
+};
+
+using std::vector;
+using o3d::MarkerFileInfo;
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Mock implementation
+
+const uint64 kInitialCurrentTime = 20000;
+const uint64 kInitialUpTime = 10000;
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class TimeManagerMock : public TimeManagerInterface {
+ public:
+ TimeManagerMock()
+ : current_time_(kInitialCurrentTime),
+ up_time_(kInitialUpTime) { }
+
+ virtual uint64 GetCurrentTime() { return current_time_; }
+ virtual uint64 GetUpTime() { return up_time_; }
+
+ void AdvanceTime(uint64 n) {
+ current_time_ += n;
+ up_time_ += n;
+ }
+
+ void SetCurrentTime(uint64 t) { current_time_ = t; }
+ void SetUpTime(uint64 t) { up_time_ = t; }
+
+ private:
+ uint64 current_time_;
+ uint64 up_time_;
+};
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class MarkerFileManagerMock : public MarkerFileManagerInterface {
+ public:
+ MarkerFileManagerMock(TimeManagerMock *time_manager)
+ : MarkerFileManagerInterface(time_manager),
+ mock_time_manager_(time_manager),
+ created_count_(0),
+ removed_count_(0) {
+ };
+
+ // "marker" file management. The marker file is used to check for future
+ // blue-screens.
+ virtual void CreateMarkerFile();
+ virtual void RemoveMarkerFile();
+
+ void AddFileEntry(const String &name, uint64 creation_time) {
+ file_list_.push_back(MarkerFileInfo(name, creation_time));
+ }
+
+ // Each should be 1 after the test is run
+ int GetCreatedCount() const {
+ return created_count_;
+ }
+
+ int GetRemovedCount() const {
+ return removed_count_;
+ }
+
+ int GetMarkerFileCount() const {
+ return file_list_.size();
+ }
+
+ private:
+ virtual bool GetMarkerFileList(vector<MarkerFileInfo> *file_list);
+ virtual void DeleteMarkerFile(const MarkerFileInfo &file_info);
+
+ TimeManagerMock *mock_time_manager_;
+ vector<MarkerFileInfo> file_list_;
+ int created_count_;
+ int removed_count_;
+};
+
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class BluescreenLoggerMock : public BluescreenLoggerInterface {
+ public:
+ BluescreenLoggerMock() : bluescreen_count_(0), log_bluescreen_count_(0) { }
+
+ // Pretends to send blue-screen info to Google
+ virtual void LogBluescreen(int num_bluescreens) {
+ ++log_bluescreen_count_; // counts number of times this method is called
+ bluescreen_count_ += num_bluescreens;
+ }
+
+ // Returns number of bluescreens logged
+ int GetBluescreenCount() const {
+ return bluescreen_count_;
+ }
+
+ // Returns total number of times LogBluescreen() was called
+ // (it should only be called once)
+ int GetLogBluescreenCount() const {
+ return log_bluescreen_count_;
+ }
+
+ private:
+ int bluescreen_count_;
+ int log_bluescreen_count_;
+};
+
+
+
+const String kOurMarkerFileName("OurMarkerFile");
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void MarkerFileManagerMock::CreateMarkerFile() {
+ AddFileEntry(kOurMarkerFileName, time_manager_->GetCurrentTime());
+ mock_time_manager_->AdvanceTime(1);
+ created_count_++;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void MarkerFileManagerMock::RemoveMarkerFile() {
+ for (int i = 0; i < file_list_.size(); ++i) {
+ if (file_list_[i].GetName() == kOurMarkerFileName) {
+ file_list_.erase(file_list_.begin() + i);
+ removed_count_++;
+ break;
+ }
+ }
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool MarkerFileManagerMock::GetMarkerFileList(
+ vector<MarkerFileInfo> *file_list) {
+ // Just duplicate our interval file list
+ for (int i = 0; i < file_list_.size(); ++i) {
+ file_list->push_back(file_list_[i]);
+ }
+ return true;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void MarkerFileManagerMock::DeleteMarkerFile(const MarkerFileInfo &file_info) {
+ for (int i = 0; i < file_list_.size(); ++i) {
+ if (file_list_[i].GetName() == file_info.GetName()) {
+ file_list_.erase(file_list_.begin() + i);
+ break;
+ }
+ }
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+// Make sure marker file is written, then removed...
+// This is also testing the case where there was a regular crash
+// (such as "bus error" or "divide-by-zero") in which case the
+// exception handler will call Stop() on the detector (instead of the plugin
+// shutdown code calling it)
+TEST_F(BluescreenDetectorTest, Basic) {
+ TimeManagerMock time_manager;
+ MarkerFileManagerMock marker_file_manager(&time_manager);
+ BluescreenLoggerMock bluescreen_logger;
+
+ BluescreenDetector detector(&time_manager,
+ &marker_file_manager,
+ &bluescreen_logger);
+
+ detector.Start();
+
+ // Make sure marker file was created (but not yet removed)
+ EXPECT_EQ(1, marker_file_manager.GetCreatedCount());
+ EXPECT_EQ(0, marker_file_manager.GetRemovedCount());
+
+ detector.Stop();
+
+ // Make sure marker file was created then removed
+ EXPECT_EQ(1, marker_file_manager.GetCreatedCount());
+ EXPECT_EQ(1, marker_file_manager.GetRemovedCount());
+
+ // we didn't add any old marker files, so there should be no bluescreens
+ EXPECT_EQ(0, bluescreen_logger.GetBluescreenCount());
+ EXPECT_EQ(0, bluescreen_logger.GetLogBluescreenCount());
+}
+
+// Let's try simulating a simple blue-screen
+TEST_F(BluescreenDetectorTest, SimulateBluescreen) {
+ TimeManagerMock time_manager;
+ MarkerFileManagerMock marker_file_manager(&time_manager);
+ BluescreenLoggerMock bluescreen_logger;
+
+ BluescreenDetector detector(&time_manager,
+ &marker_file_manager,
+ &bluescreen_logger);
+
+ // Let's create a couple of stray marker files :)
+ // and say they were created 100 time units before the machine was
+ // booted
+ uint64 kStrayCreationTime1 = kInitialCurrentTime - kInitialUpTime - 100;
+ marker_file_manager.AddFileEntry("Stray1", kStrayCreationTime1);
+ marker_file_manager.AddFileEntry("Stray2", kStrayCreationTime1);
+
+ // Verify the two we just added
+ EXPECT_EQ(2, marker_file_manager.GetMarkerFileCount());
+
+ detector.Start();
+
+ // Check that two bluescreens were detected (and reported)
+ EXPECT_EQ(2, bluescreen_logger.GetBluescreenCount());
+
+ // Check that LogBluescreen() was only called once (with two detections)
+ EXPECT_EQ(1, bluescreen_logger.GetLogBluescreenCount());
+
+ // Make sure the two "stray" marker files were removed
+ // (so we won't report bluescreens multiple times)
+ // the marker file added by |detector| should still be there
+ EXPECT_EQ(1, marker_file_manager.GetMarkerFileCount());
+
+ detector.Stop();
+
+ // Now make sure the marker file added by |detector|
+ // was also removed
+ EXPECT_EQ(0, marker_file_manager.GetMarkerFileCount());
+
+ // Make sure marker file was created then removed
+ EXPECT_EQ(1, marker_file_manager.GetCreatedCount());
+ EXPECT_EQ(1, marker_file_manager.GetRemovedCount());
+}
+
+// Let's make sure we don't detect a blue-screen from marker
+// files written since boot time (these marker files may be
+// written by o3d running in other browsers alongside ours)
+TEST_F(BluescreenDetectorTest, OtherBrowsersRunning) {
+ TimeManagerMock time_manager;
+ MarkerFileManagerMock marker_file_manager(&time_manager);
+ BluescreenLoggerMock bluescreen_logger;
+
+ BluescreenDetector detector(&time_manager,
+ &marker_file_manager,
+ &bluescreen_logger);
+
+ // Let's create a couple of other marker files :)
+ // but this time 100 time units AFTER the machine was
+ // booted, simulating other browsers which are still running
+ // o3d
+ uint64 kStrayCreationTime1 = kInitialCurrentTime - kInitialUpTime + 100;
+ marker_file_manager.AddFileEntry("OtherBrowserMarker1", kStrayCreationTime1);
+ marker_file_manager.AddFileEntry("OtherBrowserMarker2", kStrayCreationTime1);
+
+ // Verify the two we just added
+ EXPECT_EQ(2, marker_file_manager.GetMarkerFileCount());
+
+ detector.Start();
+
+ // Check that NO bluescreens were detected
+ EXPECT_EQ(0, bluescreen_logger.GetBluescreenCount());
+
+ // Check that NO bluescreen reports were logged/uploaded
+ EXPECT_EQ(0, bluescreen_logger.GetLogBluescreenCount());
+
+ // Make sure the two other marker files were NOT removed
+ // because they were not created before boot time and
+ // are owned by a different browser...
+ // There should be the two we added, plus the one added
+ // by |detector|
+ EXPECT_EQ(3, marker_file_manager.GetMarkerFileCount());
+
+ detector.Stop();
+
+ // Now make sure the marker file added by |detector|
+ // was removed, so we're left with the original two
+ EXPECT_EQ(2, marker_file_manager.GetMarkerFileCount());
+
+ // Make sure marker file was created then removed
+ EXPECT_EQ(1, marker_file_manager.GetCreatedCount());
+ EXPECT_EQ(1, marker_file_manager.GetRemovedCount());
+}
+
+} // namespace o3d
diff --git a/o3d/breakpad/win/breakpad_config.cc b/o3d/breakpad/win/breakpad_config.cc
new file mode 100644
index 0000000..6ceb73d
--- /dev/null
+++ b/o3d/breakpad/win/breakpad_config.cc
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+//
+// Breakpad configuration constants
+
+// Product-specific constants. MODIFY THESE TO SUIT YOUR PROJECT.
+
+#define _QUOTEME(x) #x
+#define QUOTEME(x) _QUOTEME(x)
+
+#define _MAKE_LONG_STRING(string) L ## string
+#define MAKE_LONG_STRING(string) _MAKE_LONG_STRING(string)
+
+#define PRODUCT_VERSION_STRING MAKE_LONG_STRING(QUOTEME(O3D_VERSION_NUMBER))
+
+// !!@ CURRENTLY WE'RE HARDCODING (win32 firefox) HERE!!
+wchar_t *kCrashReportProductName = L"O3D"; // [naming]
+
+wchar_t *kCrashReportProductVersion =
+ PRODUCT_VERSION_STRING L" (win32 firefox)";
+
+// Crash report uploading configuration (used by reporter.exe)
+
+// production server
+wchar_t *kCrashReportUrl = L"http://www.google.com/cr/report";
+
+wchar_t *kCrashReportProductParam = L"prod";
+wchar_t *kCrashReportVersionParam = L"ver";
+
+// Throttling-related constants
+// (we don't want to upload too many crash reports in a row)
+
+bool kCrashReportAlwaysUpload = false; // disables throttling if set to |true|
+
+wchar_t *kCrashReportThrottlingRegKey =
+ L"Software\\Google\\Breakpad\\Throttling";
+
+int kCrashReportAttempts = 3;
+int kCrashReportResendPeriodMs = (1 * 60 * 60 * 1000);
+
+// kCrashReportsMaxPerInterval is defined (using #define)
+// in the header file, since the compiler requires it to be a constant
+// (can't be global variable as defined in this file)
+
+int kCrashReportsIntervalSeconds = (24 * 60 * 60);
diff --git a/o3d/breakpad/win/breakpad_config.h b/o3d/breakpad/win/breakpad_config.h
new file mode 100644
index 0000000..5b28801
--- /dev/null
+++ b/o3d/breakpad/win/breakpad_config.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+//
+// Breakpad configuration constants
+
+#ifndef O3D_BREAKPAD_WIN_BREAKPAD_CONFIG_H_
+#define O3D_BREAKPAD_WIN_BREAKPAD_CONFIG_H_
+
+extern wchar_t *kCrashReportProductName;
+extern wchar_t *kCrashReportProductVersion;
+
+extern wchar_t *kReporterPath;
+
+// Crash report uploading configuration (used by reporter.exe)
+extern wchar_t *kCrashReportUrl;
+extern wchar_t *kCrashReportStagingUrl;
+extern wchar_t *kCrashReportProductParam;
+extern wchar_t *kCrashReportVersionParam;
+
+// Throttling-related constants
+extern bool kCrashReportAlwaysUpload;
+extern wchar_t *kCrashReportThrottlingRegKey;
+extern int kCrashReportAttempts;
+extern int kCrashReportResendPeriodMs;
+#define kCrashReportsMaxPerInterval 5
+extern int kCrashReportsIntervalSeconds;
+
+#endif // O3D_BREAKPAD_WIN_BREAKPAD_CONFIG_H_
diff --git a/o3d/breakpad/win/crash_sender_win32.cc b/o3d/breakpad/win/crash_sender_win32.cc
new file mode 100644
index 0000000..4d62e5f
--- /dev/null
+++ b/o3d/breakpad/win/crash_sender_win32.cc
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+//
+// Breakpad crash report uploader
+// (adapted from code in Google Gears)
+//
+
+
+#include <map>
+#include <windows.h>
+#include <assert.h>
+#include <shellapi.h>
+#include <tchar.h>
+#include <time.h>
+
+#include "breakpad_config.h"
+#include "client/windows/sender/crash_report_sender.h"
+
+using std::map;
+using std::wstring;
+using google_breakpad::CrashReportSender;
+using google_breakpad::ReportResult;
+
+bool CanSendMinidump(const wchar_t *product_name);
+void SendMinidump(const wchar_t *minidump_filename,
+ const wchar_t *product_name,
+ const wchar_t *product_version);
+
+
+int _tmain(int argc, _TCHAR* argv[]) {
+ SendMinidump(argv[1], argv[2], argv[3]);
+ return 0;
+}
+
+bool CanSendMinidump(const wchar_t *product_name) {
+ // useful for testing purposes
+ if (kCrashReportAlwaysUpload) {
+ return true;
+ }
+
+ bool can_send = false;
+
+ time_t current_time;
+ time(&current_time);
+
+ // For throttling, we remember when the last N minidumps were sent.
+
+ time_t past_send_times[kCrashReportsMaxPerInterval];
+ DWORD bytes = sizeof(past_send_times);
+ memset(&past_send_times, 0, bytes);
+
+ HKEY reg_key;
+ DWORD create_type;
+ if (ERROR_SUCCESS != RegCreateKeyExW(HKEY_CURRENT_USER,
+ kCrashReportThrottlingRegKey,
+ 0,
+ NULL,
+ 0,
+ KEY_READ | KEY_WRITE, NULL,
+ &reg_key, &create_type)) {
+ return false; // this should never happen, but just in case
+ }
+
+ if (ERROR_SUCCESS != RegQueryValueEx(reg_key, product_name, NULL,
+ NULL,
+ reinterpret_cast<BYTE*>(past_send_times),
+ &bytes)) {
+ // this product hasn't sent any crash reports yet
+ can_send = true;
+ } else {
+ // find crash reports within the last interval
+ int crashes_in_last_interval = 0;
+ for (int i = 0; i < kCrashReportsMaxPerInterval; ++i) {
+ if (current_time - past_send_times[i] < kCrashReportsIntervalSeconds) {
+ ++crashes_in_last_interval;
+ }
+ }
+
+ can_send = crashes_in_last_interval < kCrashReportsMaxPerInterval;
+ }
+
+ if (can_send) {
+ memmove(&past_send_times[1],
+ &past_send_times[0],
+ sizeof(time_t) * (kCrashReportsMaxPerInterval - 1));
+ past_send_times[0] = current_time;
+ }
+
+ RegSetValueEx(reg_key, product_name, 0, REG_BINARY,
+ reinterpret_cast<BYTE*>(past_send_times),
+ sizeof(past_send_times));
+
+ return can_send;
+}
+
+void SendMinidump(const wchar_t *minidump_filename,
+ const wchar_t *product_name,
+ const wchar_t *product_version) {
+ if (CanSendMinidump(product_name)) {
+ map<std::wstring, std::wstring> parameters;
+ parameters[kCrashReportProductParam] = product_name;
+ parameters[kCrashReportVersionParam] = std::wstring(product_version);
+
+ std::wstring minidump_wstr(minidump_filename);
+
+ CrashReportSender sender(L"");
+
+ for (int i = 0; i < kCrashReportAttempts; ++i) {
+ wstring upload_result;
+ ReportResult result = sender.SendCrashReport(kCrashReportUrl,
+ parameters,
+ minidump_wstr,
+ &upload_result);
+ if (result == google_breakpad::RESULT_FAILED) {
+ Sleep(kCrashReportResendPeriodMs);
+ } else {
+ // RESULT_SUCCEEDED or RESULT_REJECTED
+ break;
+ }
+ }
+ }
+
+ DeleteFileW(minidump_filename);
+}
diff --git a/o3d/breakpad/win/exception_handler_win32.cc b/o3d/breakpad/win/exception_handler_win32.cc
new file mode 100644
index 0000000..96851f0
--- /dev/null
+++ b/o3d/breakpad/win/exception_handler_win32.cc
@@ -0,0 +1,210 @@
+/*
+ * 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,
+ CSIDL_APPDATA,
+ NULL,
+ 0,
+ reporterPath);
+
+ if (result == 0) {
+ PathAppend(reporterPath, _T("Google\\O3D\\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
+}
diff --git a/o3d/breakpad/win/exception_handler_win32.h b/o3d/breakpad/win/exception_handler_win32.h
new file mode 100644
index 0000000..9986023
--- /dev/null
+++ b/o3d/breakpad/win/exception_handler_win32.h
@@ -0,0 +1,76 @@
+/*
+ * 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)
+//
+
+#ifndef O3D_BREAKPAD_WIN_EXCEPTION_HANDLER_WIN32_H_
+#define O3D_BREAKPAD_WIN_EXCEPTION_HANDLER_WIN32_H_
+
+namespace google_breakpad {
+class ExceptionHandler;
+};
+
+// Sample usage:
+// int main(void) {
+// static ExceptionManager exception_manager(false);
+// exception_manager.StartMonitoring();
+// ...
+// }
+class ExceptionManager {
+ public:
+ // If catch_entire_process is true, then all minidumps are captured.
+ // Otherwise, only crashes in this module are captured.
+ // Use the latter when running inside IE or Firefox.
+ // StartMonitoring needs to be called before any minidumps are captured.
+ explicit ExceptionManager(bool catch_entire_process);
+ ~ExceptionManager();
+
+ // Starts monitoring for crashes. When a crash occurs a minidump will
+ // automatically be captured and sent.
+ void StartMonitoring();
+
+ // TODO: Cleanup. The following should not be called
+ // directly, ideally these should be private methods.
+ bool catch_entire_process() { return catch_entire_process_; }
+ static void SendMinidump(const char *minidump_filename);
+ static bool CanSendMinidump(); // considers throttling
+
+ private:
+ static ExceptionManager *instance_;
+
+ bool catch_entire_process_;
+ google_breakpad::ExceptionHandler *exception_handler_;
+};
+
+#endif // O3D_BREAKPAD_WIN_EXCEPTION_HANDLER_WIN32_H_