aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbryner <bryner@4c0a9323-5329-0410-9bdc-e9ce6186880e>2006-09-27 01:00:32 +0000
committerbryner <bryner@4c0a9323-5329-0410-9bdc-e9ce6186880e>2006-09-27 01:00:32 +0000
commit1217c1f898457689024c6d0f7448442e7d964f86 (patch)
tree90e1adb82c0ca8d1b35d156e6814bf7c6800963e
parentBetter testing for Stackwalker (#18). r=bryner (diff)
downloadbreakpad-1217c1f898457689024c6d0f7448442e7d964f86.tar.xz
Initial version of Windows exception handler and crash report sender classes
(#31). r=mmentovai. git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@36 4c0a9323-5329-0410-9bdc-e9ce6186880e
-rw-r--r--src/client/windows/README22
-rw-r--r--src/client/windows/airbag_client.sln26
-rw-r--r--src/client/windows/handler/exception_handler.cc148
-rw-r--r--src/client/windows/handler/exception_handler.h139
-rw-r--r--src/client/windows/handler/exception_handler.vcproj175
-rw-r--r--src/client/windows/sender/crash_report_sender.cc264
-rw-r--r--src/client/windows/sender/crash_report_sender.h99
-rw-r--r--src/client/windows/sender/crash_report_sender.vcproj175
8 files changed, 1048 insertions, 0 deletions
diff --git a/src/client/windows/README b/src/client/windows/README
new file mode 100644
index 00000000..380324fd
--- /dev/null
+++ b/src/client/windows/README
@@ -0,0 +1,22 @@
+This directory contains client code for collecting and sending minidumps.
+You can use them in your project as follows:
+
+exception_handler.lib:
+
+This library should be linked into the application from which you want
+to collect crash reports. See the comments in exception_handler.h for
+instructions on setting up the handler to write minidumps on crash.
+You can also use ExceptionHandler to write non-crash minidumps.
+
+When linking with this static library, you will need to also link with
+rpcrt4.lib and ole32.lib.
+
+crash_report_sender.lib:
+
+Typically, after catching a crash, you will launch an external application
+to collect any additional information from the user, and upload the report to
+the server. CrashReportSender is a utility class that can be used when the
+data is ready to upload.
+
+When linking with this static library, you will need to also link with
+wininet.lib.
diff --git a/src/client/windows/airbag_client.sln b/src/client/windows/airbag_client.sln
new file mode 100644
index 00000000..502143bc
--- /dev/null
+++ b/src/client/windows/airbag_client.sln
@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "exception_handler", "handler\exception_handler.vcproj", "{B55CA863-B374-4BAF-95AC-539E4FA4C90C}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crash_report_sender", "sender\crash_report_sender.vcproj", "{9946A048-043B-4F8F-9E07-9297B204714C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B55CA863-B374-4BAF-95AC-539E4FA4C90C}.Debug|Win32.ActiveCfg = Debug|Win32
+ {B55CA863-B374-4BAF-95AC-539E4FA4C90C}.Debug|Win32.Build.0 = Debug|Win32
+ {B55CA863-B374-4BAF-95AC-539E4FA4C90C}.Release|Win32.ActiveCfg = Release|Win32
+ {B55CA863-B374-4BAF-95AC-539E4FA4C90C}.Release|Win32.Build.0 = Release|Win32
+ {9946A048-043B-4F8F-9E07-9297B204714C}.Debug|Win32.ActiveCfg = Debug|Win32
+ {9946A048-043B-4F8F-9E07-9297B204714C}.Debug|Win32.Build.0 = Debug|Win32
+ {9946A048-043B-4F8F-9E07-9297B204714C}.Release|Win32.ActiveCfg = Release|Win32
+ {9946A048-043B-4F8F-9E07-9297B204714C}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/client/windows/handler/exception_handler.cc b/src/client/windows/handler/exception_handler.cc
new file mode 100644
index 00000000..ba5c2653
--- /dev/null
+++ b/src/client/windows/handler/exception_handler.cc
@@ -0,0 +1,148 @@
+// Copyright (c) 2006, 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.
+
+#include <assert.h>
+
+#include <cstdio>
+
+#include "client/windows/handler/exception_handler.h"
+
+namespace google_airbag {
+
+ExceptionHandler *ExceptionHandler::current_handler_ = NULL;
+
+ExceptionHandler::ExceptionHandler(const wstring &dump_path,
+ MinidumpCallback callback,
+ void *callback_context,
+ bool install_handler)
+ : callback_(callback), callback_context_(callback_context),
+ dump_path_(dump_path), next_minidump_id_(NULL),
+ dbghelp_module_(NULL), minidump_write_dump_(NULL),
+ previous_handler_(current_handler_), previous_filter_(NULL) {
+ UpdateNextID();
+ dbghelp_module_ = LoadLibrary(L"dbghelp.dll");
+ if (dbghelp_module_) {
+ minidump_write_dump_ = reinterpret_cast<MiniDumpWriteDump_type>(
+ GetProcAddress(dbghelp_module_, "MiniDumpWriteDump"));
+ }
+ if (install_handler) {
+ previous_filter_ = SetUnhandledExceptionFilter(HandleException);
+ current_handler_ = this;
+ }
+}
+
+ExceptionHandler::~ExceptionHandler() {
+ if (dbghelp_module_) {
+ FreeLibrary(dbghelp_module_);
+ }
+ if (next_minidump_id_) {
+ RpcStringFree(&next_minidump_id_);
+ }
+ if (current_handler_ == this) {
+ SetUnhandledExceptionFilter(previous_filter_);
+ current_handler_ = previous_handler_;
+ }
+}
+
+// static
+LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS *exinfo) {
+ if (!current_handler_->WriteMinidumpWithException(exinfo)) {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+bool ExceptionHandler::WriteMinidump() {
+ bool success = WriteMinidumpWithException(NULL);
+ UpdateNextID();
+ return success;
+}
+
+// static
+bool ExceptionHandler::WriteMinidump(const wstring &dump_path,
+ MinidumpCallback callback,
+ void *callback_context) {
+ ExceptionHandler handler(dump_path, callback, callback_context, false);
+ return handler.WriteMinidump();
+}
+
+bool ExceptionHandler::WriteMinidumpWithException(EXCEPTION_POINTERS *exinfo) {
+ wchar_t dump_file_name[MAX_PATH];
+ swprintf_s(dump_file_name, MAX_PATH, L"%s\\%s.dmp",
+ dump_path_.c_str(), next_minidump_id_);
+
+ bool success = false;
+ if (minidump_write_dump_) {
+ HANDLE dump_file = CreateFile(dump_file_name,
+ GENERIC_WRITE,
+ FILE_SHARE_WRITE,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if (dump_file != INVALID_HANDLE_VALUE) {
+ MINIDUMP_EXCEPTION_INFORMATION except_info;
+ except_info.ThreadId = GetCurrentThreadId();
+ except_info.ExceptionPointers = exinfo;
+ except_info.ClientPointers = FALSE;
+
+ // The explicit comparison to TRUE avoids a warning (C4800).
+ success = (minidump_write_dump_(GetCurrentProcess(),
+ GetCurrentProcessId(),
+ dump_file,
+ MiniDumpNormal,
+ &except_info,
+ NULL,
+ NULL) == TRUE);
+
+ CloseHandle(dump_file);
+ }
+ }
+
+ if (callback_) {
+ // This looks nasty, but RPC_WSTR is really just a wide string,
+ // and there are no "supported" ways to convert them other than casting.
+ callback_(reinterpret_cast<wchar_t*>(next_minidump_id_),
+ callback_context_, success);
+ }
+ // TODO(bryner): log an error on failure
+
+ return success;
+}
+
+void ExceptionHandler::UpdateNextID() {
+ if (next_minidump_id_) {
+ RpcStringFree(&next_minidump_id_);
+ }
+ GUID id;
+ CoCreateGuid(&id);
+ UuidToString(&id, &next_minidump_id_);
+}
+
+} // namespace google_airbag
diff --git a/src/client/windows/handler/exception_handler.h b/src/client/windows/handler/exception_handler.h
new file mode 100644
index 00000000..7acb5853
--- /dev/null
+++ b/src/client/windows/handler/exception_handler.h
@@ -0,0 +1,139 @@
+// Copyright (c) 2006, 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.
+
+// ExceptionHandler can write a minidump file when an exception occurs,
+// or when WriteMinidump() is called explicitly by your program.
+//
+// To have the exception handler write minidumps when an uncaught exception
+// (crash) occurs, you should create an instance early in the execution
+// of your program, and keep it around for the entire time you want to
+// have crash handling active (typically, until shutdown).
+//
+// If you want to write minidumps without installing the exception handler,
+// you can create an ExceptionHandler with install_handler set to false,
+// then call WriteMinidump. You can also use this technique if you want to
+// use different minidump callbacks for different call sites.
+//
+// In either case, a callback function is called when a minidump is written,
+// which receives the unqiue id of the minidump. The caller can use this
+// id to collect and write additional application state, and to launch an
+// external crash-reporting application.
+//
+// It is important that creation and destruction of ExceptionHandler objects
+// be nested cleanly, when using install_handler = true.
+// Avoid the following pattern:
+// ExceptionHandler *e = new ExceptionHandler(...);
+// ExceptionHandler *f = new ExceptionHandler(...);
+// delete e;
+// This will put the exception filter stack into an inconsistent state.
+
+#ifndef CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__
+#define CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#include <string>
+
+namespace google_airbag {
+
+using std::wstring;
+
+class ExceptionHandler {
+ public:
+ // A callback function to run after the minidump has been written.
+ // minidump_id is a unique id for the dump, so the minidump
+ // file is <dump_path>\<minidump_id>.dmp. succeeded indicates whether
+ // a minidump file was successfully written.
+ typedef void (*MinidumpCallback)(const wstring &minidump_id,
+ void *context, bool succeeded);
+
+ // Creates a new ExceptionHandler instance to handle writing minidumps.
+ // Minidump files will be written to dump_path, and the optional callback
+ // is called after writing the dump file, as described above.
+ // If install_handler is true, then a minidump will be written whenever
+ // an unhandled exception occurs. If it is false, minidumps will only
+ // be written when WriteMinidump is called.
+ ExceptionHandler(const wstring &dump_path, MinidumpCallback callback,
+ void *callback_context, bool install_handler);
+ ~ExceptionHandler();
+
+ // Writes a minidump immediately. This can be used to capture the
+ // execution state independently of a crash. Returns true on success.
+ bool WriteMinidump();
+
+ // Convenience form of WriteMinidump which does not require an
+ // ExceptionHandler instance.
+ static bool WriteMinidump(const wstring &dump_path,
+ MinidumpCallback callback, void *callback_context);
+
+ private:
+ // Function pointer type for MiniDumpWriteDump, which is looked up
+ // dynamically.
+ typedef BOOL (WINAPI *MiniDumpWriteDump_type)(
+ HANDLE hProcess,
+ DWORD dwPid,
+ HANDLE hFile,
+ MINIDUMP_TYPE DumpType,
+ CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
+ CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
+ CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
+
+ // This function does the actual writing of a minidump.
+ bool WriteMinidumpWithException(EXCEPTION_POINTERS *exinfo);
+
+ // Called when an unhandled exception occurs.
+ static LONG WINAPI HandleException(EXCEPTION_POINTERS *exinfo);
+
+ // Generates a new ID and stores it in next_minidump_id_.
+ void UpdateNextID();
+
+ MinidumpCallback callback_;
+ void *callback_context_;
+
+ wstring dump_path_;
+ RPC_WSTR next_minidump_id_;
+
+ HMODULE dbghelp_module_;
+ MiniDumpWriteDump_type minidump_write_dump_;
+
+ ExceptionHandler *previous_handler_; // current_handler_ before us
+ LPTOP_LEVEL_EXCEPTION_FILTER previous_filter_;
+
+ // the currently-installed ExceptionHandler, of which there can be only 1
+ static ExceptionHandler *current_handler_;
+
+ // disallow copy ctor and operator=
+ explicit ExceptionHandler(const ExceptionHandler &);
+ void operator=(const ExceptionHandler &);
+};
+
+} // namespace google_airbag
+
+#endif // CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__
diff --git a/src/client/windows/handler/exception_handler.vcproj b/src/client/windows/handler/exception_handler.vcproj
new file mode 100644
index 00000000..5dce6273
--- /dev/null
+++ b/src/client/windows/handler/exception_handler.vcproj
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="exception_handler"
+ ProjectGUID="{B55CA863-B374-4BAF-95AC-539E4FA4C90C}"
+ RootNamespace="exception_handler"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\.."
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\.."
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\exception_handler.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\exception_handler.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/src/client/windows/sender/crash_report_sender.cc b/src/client/windows/sender/crash_report_sender.cc
new file mode 100644
index 00000000..16c26c2b
--- /dev/null
+++ b/src/client/windows/sender/crash_report_sender.cc
@@ -0,0 +1,264 @@
+// Copyright (c) 2006, 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.
+
+#include <assert.h>
+#include <windows.h>
+#include <wininet.h>
+
+#include <fstream>
+
+#include "client/windows/sender/crash_report_sender.h"
+
+namespace google_airbag {
+
+using std::ifstream;
+using std::ios;
+
+static const wchar_t kUserAgent[] = L"Airbag/1.0 (Windows)";
+
+// Helper class which closes an internet handle when it goes away
+class CrashReportSender::AutoInternetHandle {
+ public:
+ explicit AutoInternetHandle(HINTERNET handle) : handle_(handle) {}
+ ~AutoInternetHandle() {
+ if (handle_) {
+ InternetCloseHandle(handle_);
+ }
+ }
+
+ HINTERNET get() { return handle_; }
+
+ private:
+ HINTERNET handle_;
+};
+
+// static
+bool CrashReportSender::SendCrashReport(
+ const wstring &url, const map<wstring, wstring> &parameters,
+ const wstring &dump_file_name) {
+ // TODO(bryner): support non-ASCII parameter names
+ if (!CheckParameters(parameters)) {
+ return false;
+ }
+
+ // Break up the URL and make sure we can handle it
+ wchar_t scheme[16], host[256], path[256];
+ URL_COMPONENTS components;
+ memset(&components, 0, sizeof(components));
+ components.dwStructSize = sizeof(components);
+ components.lpszScheme = scheme;
+ components.dwSchemeLength = sizeof(scheme);
+ components.lpszHostName = host;
+ components.dwHostNameLength = sizeof(host);
+ components.lpszUrlPath = path;
+ components.dwUrlPathLength = sizeof(path);
+ if (!InternetCrackUrl(url.c_str(), static_cast<DWORD>(url.size()),
+ 0, &components)) {
+ return false;
+ }
+ if (wcscmp(scheme, L"http") != 0) {
+ return false;
+ }
+
+ AutoInternetHandle internet(InternetOpen(kUserAgent,
+ INTERNET_OPEN_TYPE_DIRECT,
+ NULL, // proxy name
+ NULL, // proxy bypass
+ 0)); // flags
+ if (!internet.get()) {
+ return false;
+ }
+
+ AutoInternetHandle connection(InternetConnect(internet.get(),
+ host,
+ components.nPort,
+ NULL, // user name
+ NULL, // password
+ INTERNET_SERVICE_HTTP,
+ 0, // flags
+ NULL)); // context
+ if (!connection.get()) {
+ return false;
+ }
+
+ AutoInternetHandle request(HttpOpenRequest(connection.get(),
+ L"POST",
+ path,
+ NULL, // version
+ NULL, // referer
+ NULL, // agent type
+ 0, // flags
+ NULL)); // context
+ if (!request.get()) {
+ return false;
+ }
+
+ wstring boundary = GenerateMultipartBoundary();
+ wstring content_type_header = GenerateRequestHeader(boundary);
+ HttpAddRequestHeaders(request.get(),
+ content_type_header.c_str(),
+ -1, HTTP_ADDREQ_FLAG_ADD);
+
+ string request_body;
+ GenerateRequestBody(parameters, dump_file_name, boundary, &request_body);
+
+ // The explicit comparison to TRUE avoids a warning (C4800).
+ return (HttpSendRequest(request.get(), NULL, 0,
+ const_cast<char *>(request_body.data()),
+ static_cast<DWORD>(request_body.size())) == TRUE);
+}
+
+// static
+wstring CrashReportSender::GenerateMultipartBoundary() {
+ // The boundary has 27 '-' characters followed by 16 hex digits
+ static const wchar_t kBoundaryPrefix[] = L"---------------------------";
+ static const int kBoundaryLength = 27 + 16 + 1;
+
+ // Generate some random numbers to fill out the boundary
+ int r0 = rand();
+ int r1 = rand();
+
+ wchar_t temp[kBoundaryLength];
+ swprintf_s(temp, kBoundaryLength, L"%s%08X%08X", kBoundaryPrefix, r0, r1);
+ return wstring(temp);
+}
+
+// static
+wstring CrashReportSender::GenerateRequestHeader(const wstring &boundary) {
+ wstring header = L"Content-Type: multipart/form-data; boundary=";
+ header += boundary;
+ return header;
+}
+
+// static
+bool CrashReportSender::GenerateRequestBody(
+ const map<wstring, wstring> &parameters,
+ const wstring &minidump_filename, const wstring &boundary,
+ string *request_body) {
+ vector<char> contents;
+ GetFileContents(minidump_filename, &contents);
+ if (contents.empty()) {
+ return false;
+ }
+
+ string boundary_str = WideToUTF8(boundary);
+ if (boundary_str.empty()) {
+ return false;
+ }
+
+ request_body->clear();
+
+ // Append each of the parameter pairs as a form-data part
+ for (map<wstring, wstring>::const_iterator pos = parameters.begin();
+ pos != parameters.end(); ++pos) {
+ request_body->append("--" + boundary_str + "\r\n");
+ request_body->append("Content-Disposition: form-data; name=\"" +
+ WideToUTF8(pos->first) + "\"\r\n\r\n" +
+ WideToUTF8(pos->second) + "\r\n");
+ }
+
+ // Now append the minidump file as a binary (octet-stream) part
+ string filename_utf8 = WideToUTF8(minidump_filename);
+ if (filename_utf8.empty()) {
+ return false;
+ }
+
+ request_body->append("--" + boundary_str + "\r\n");
+ request_body->append("Content-Disposition: form-data; "
+ "name=\"upload_file_minidump\"; "
+ "filename=\"" + filename_utf8 + "\"\r\n");
+ request_body->append("Content-Type: application/octet-stream\r\n");
+ request_body->append("\r\n");
+
+ request_body->append(&(contents[0]), contents.size());
+ request_body->append("\r\n");
+ request_body->append("--" + boundary_str + "--\r\n");
+ return true;
+}
+
+// static
+void CrashReportSender::GetFileContents(const wstring &filename,
+ vector<char> *contents) {
+ ifstream file;
+ file.open(filename.c_str(), ios::binary);
+ if (file.is_open()) {
+ file.seekg(0, ios::end);
+ int length = file.tellg();
+ contents->resize(length);
+ file.seekg(0, ios::beg);
+ file.read(&((*contents)[0]), length);
+ file.close();
+ } else {
+ contents->clear();
+ }
+}
+
+// static
+string CrashReportSender::WideToUTF8(const wstring &wide) {
+ if (wide.length() == 0) {
+ return string();
+ }
+
+ // compute the length of the buffer we'll need
+ int charcount = WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), -1,
+ NULL, 0, NULL, NULL);
+ if (charcount == 0) {
+ return string();
+ }
+
+ // convert
+ char *buf = new char[charcount];
+ WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), -1, buf, charcount,
+ NULL, NULL);
+
+ string result(buf);
+ delete[] buf;
+ return result;
+}
+
+// static
+bool CrashReportSender::CheckParameters(
+ const map<wstring, wstring> &parameters) {
+ for (map<wstring, wstring>::const_iterator pos = parameters.begin();
+ pos != parameters.end(); ++pos) {
+ const wstring &str = pos->first;
+ if (str.size() == 0) {
+ return false; // disallow empty parameter names
+ }
+ for (unsigned int i = 0; i < str.size(); ++i) {
+ wchar_t c = str[i];
+ if (c < 32 || c == '"' || c > 127) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace google_airbag
diff --git a/src/client/windows/sender/crash_report_sender.h b/src/client/windows/sender/crash_report_sender.h
new file mode 100644
index 00000000..accd7b46
--- /dev/null
+++ b/src/client/windows/sender/crash_report_sender.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2006, 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.
+
+#ifndef CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__
+#define CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__
+
+// CrashReportSender is a "static" class which provides an API to upload
+// crash reports via HTTP. A crash report is formatted as a multipart POST
+// request, which contains a set of caller-supplied string key/value pairs,
+// and a minidump file to upload.
+
+#include <string>
+#include <map>
+#include <vector>
+
+namespace google_airbag {
+
+using std::string;
+using std::wstring;
+using std::map;
+using std::vector;
+
+class CrashReportSender {
+ public:
+ // Sends the specified minidump file, along with the map of
+ // name value pairs, as a multipart POST request to the given URL.
+ // Parameter names must contain only printable ASCII characters,
+ // and may not contain a quote (") character.
+ // Only HTTP URLs are currently supported. Returns true on success.
+ // TODO(bryner): we should expose the response to the caller.
+ static bool SendCrashReport(const wstring &url,
+ const map<wstring, wstring> &parameters,
+ const wstring &dump_file_name);
+
+ private:
+ class AutoInternetHandle;
+
+ // Generates a new multipart boundary for a POST request
+ static wstring GenerateMultipartBoundary();
+
+ // Generates a HTTP request header for a multipart form submit.
+ static wstring GenerateRequestHeader(const wstring &boundary);
+
+ // Given a set of parameters and a minidump file name,
+ // generates a multipart request body string with these parameters
+ // and minidump contents. Returns true on success.
+ static bool GenerateRequestBody(const map<wstring, wstring> &parameters,
+ const wstring &minidump_filename,
+ const wstring &boundary,
+ string *request_body);
+
+ // Fills the supplied vector with the contents of filename.
+ static void GetFileContents(const wstring &filename, vector<char> *contents);
+
+ // Converts a UTF16 string to UTF8.
+ static string WideToUTF8(const wstring &wide);
+
+ // Checks that the given list of parameters has only printable
+ // ASCII characters in the parameter name, and does not contain
+ // any quote (") characters. Returns true if so.
+ static bool CheckParameters(const map<wstring, wstring> &parameters);
+
+ // No instances of this class should be created.
+ // Disallow all constructors, destructors, and operator=.
+ CrashReportSender();
+ explicit CrashReportSender(const CrashReportSender &);
+ void operator=(const CrashReportSender &);
+ ~CrashReportSender();
+};
+
+} // namespace google_airbag
+
+#endif // CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__
diff --git a/src/client/windows/sender/crash_report_sender.vcproj b/src/client/windows/sender/crash_report_sender.vcproj
new file mode 100644
index 00000000..8784501e
--- /dev/null
+++ b/src/client/windows/sender/crash_report_sender.vcproj
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="crash_report_sender"
+ ProjectGUID="{9946A048-043B-4F8F-9E07-9297B204714C}"
+ RootNamespace="crash_report_sender"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\.."
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\.."
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\crash_report_sender.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\crash_report_sender.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>