diff options
author | gspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-27 23:15:42 +0000 |
---|---|---|
committer | gspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-27 23:15:42 +0000 |
commit | 05b47f7a8c5451f858dc220df0e3a97542edace6 (patch) | |
tree | a2273d619f0625c9d44d40842845ccce2eac1045 /o3d/breakpad | |
parent | 5cdc8bdb4c847cefe7f4542bd10c9880c2c557a0 (diff) | |
download | chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.zip chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.gz chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.bz2 |
This is the O3D source tree's initial commit to the Chromium tree. It
is not built or referenced at all by the chrome build yet, and doesn't
yet build in it's new home. We'll change that shortly.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17035 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/breakpad')
-rw-r--r-- | o3d/breakpad/build.scons | 86 | ||||
-rw-r--r-- | o3d/breakpad/win/bluescreen_detector.cc | 331 | ||||
-rw-r--r-- | o3d/breakpad/win/bluescreen_detector.h | 219 | ||||
-rw-r--r-- | o3d/breakpad/win/bluescreen_detector_test.cc | 322 | ||||
-rw-r--r-- | o3d/breakpad/win/breakpad_config.cc | 74 | ||||
-rw-r--r-- | o3d/breakpad/win/breakpad_config.h | 57 | ||||
-rw-r--r-- | o3d/breakpad/win/crash_sender_win32.cc | 153 | ||||
-rw-r--r-- | o3d/breakpad/win/exception_handler_win32.cc | 210 | ||||
-rw-r--r-- | o3d/breakpad/win/exception_handler_win32.h | 76 |
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(¤t_time); + + FILETIME file_time; + ::SystemTimeToFileTime(¤t_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(¤t_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, + ®_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_ |