diff options
author | bryner <bryner@4c0a9323-5329-0410-9bdc-e9ce6186880e> | 2006-09-27 01:00:32 +0000 |
---|---|---|
committer | bryner <bryner@4c0a9323-5329-0410-9bdc-e9ce6186880e> | 2006-09-27 01:00:32 +0000 |
commit | 1217c1f898457689024c6d0f7448442e7d964f86 (patch) | |
tree | 90e1adb82c0ca8d1b35d156e6814bf7c6800963e | |
parent | Better testing for Stackwalker (#18). r=bryner (diff) | |
download | breakpad-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/README | 22 | ||||
-rw-r--r-- | src/client/windows/airbag_client.sln | 26 | ||||
-rw-r--r-- | src/client/windows/handler/exception_handler.cc | 148 | ||||
-rw-r--r-- | src/client/windows/handler/exception_handler.h | 139 | ||||
-rw-r--r-- | src/client/windows/handler/exception_handler.vcproj | 175 | ||||
-rw-r--r-- | src/client/windows/sender/crash_report_sender.cc | 264 | ||||
-rw-r--r-- | src/client/windows/sender/crash_report_sender.h | 99 | ||||
-rw-r--r-- | src/client/windows/sender/crash_report_sender.vcproj | 175 |
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> ¶meters, + 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> ¶meters, + 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> ¶meters) { + 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> ¶meters, + 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> ¶meters, + 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> ¶meters); + + // 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>
|