diff --git a/src/client/windows/sender/crash_report_sender.cc b/src/client/windows/sender/crash_report_sender.cc index 16c26c2b..2a4f8250 100644 --- a/src/client/windows/sender/crash_report_sender.cc +++ b/src/client/windows/sender/crash_report_sender.cc @@ -27,238 +27,18 @@ // (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 -#include -#include - -#include - #include "client/windows/sender/crash_report_sender.h" +#include "common/windows/http_upload.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 ¶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(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(request_body.data()), - static_cast(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 ¶meters, - const wstring &minidump_filename, const wstring &boundary, - string *request_body) { - vector 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::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 *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 ¶meters) { - for (map::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; + return HTTPUpload::SendRequest(url, parameters, dump_file_name, + L"upload_file_minidump"); } } // namespace google_airbag diff --git a/src/client/windows/sender/crash_report_sender.h b/src/client/windows/sender/crash_report_sender.h index 52bec788..39267243 100644 --- a/src/client/windows/sender/crash_report_sender.h +++ b/src/client/windows/sender/crash_report_sender.h @@ -40,14 +40,11 @@ #include #include -#include namespace google_airbag { -using std::string; using std::wstring; using std::map; -using std::vector; class CrashReportSender { public: @@ -62,33 +59,6 @@ class CrashReportSender { 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 ¶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 *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 ¶meters); - // No instances of this class should be created. // Disallow all constructors, destructors, and operator=. CrashReportSender(); diff --git a/src/client/windows/sender/crash_report_sender.vcproj b/src/client/windows/sender/crash_report_sender.vcproj index eabb76ce..fc131ef5 100644 --- a/src/client/windows/sender/crash_report_sender.vcproj +++ b/src/client/windows/sender/crash_report_sender.vcproj @@ -276,6 +276,10 @@ RelativePath=".\crash_report_sender.cc" > + + + + +#include +#include + +#include + +#include "common/windows/http_upload.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 HTTPUpload::AutoInternetHandle { + public: + explicit AutoInternetHandle(HINTERNET handle) : handle_(handle) {} + ~AutoInternetHandle() { + if (handle_) { + InternetCloseHandle(handle_); + } + } + + HINTERNET get() { return handle_; } + + private: + HINTERNET handle_; +}; + +// static +bool HTTPUpload::SendRequest(const wstring &url, + const map ¶meters, + const wstring &upload_file, + const wstring &file_part_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(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, upload_file, + file_part_name, boundary, &request_body); + + // The explicit comparison to TRUE avoids a warning (C4800). + return (HttpSendRequest(request.get(), NULL, 0, + const_cast(request_body.data()), + static_cast(request_body.size())) == TRUE); +} + +// static +wstring HTTPUpload::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 HTTPUpload::GenerateRequestHeader(const wstring &boundary) { + wstring header = L"Content-Type: multipart/form-data; boundary="; + header += boundary; + return header; +} + +// static +bool HTTPUpload::GenerateRequestBody(const map ¶meters, + const wstring &upload_file, + const wstring &file_part_name, + const wstring &boundary, + string *request_body) { + vector contents; + GetFileContents(upload_file, &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::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 upload file as a binary (octet-stream) part + string filename_utf8 = WideToUTF8(upload_file); + if (filename_utf8.empty()) { + return false; + } + + string file_part_name_utf8 = WideToUTF8(file_part_name); + if (file_part_name_utf8.empty()) { + return false; + } + + request_body->append("--" + boundary_str + "\r\n"); + request_body->append("Content-Disposition: form-data; " + "name=\"" + file_part_name_utf8 + "\"; " + "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 HTTPUpload::GetFileContents(const wstring &filename, + vector *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 HTTPUpload::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 HTTPUpload::CheckParameters(const map ¶meters) { + for (map::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/common/windows/http_upload.h b/src/common/windows/http_upload.h new file mode 100644 index 00000000..4ce581ea --- /dev/null +++ b/src/common/windows/http_upload.h @@ -0,0 +1,101 @@ +// 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. + +// HTTPUpload provides a "nice" API to send a multipart HTTP POST +// request using wininet. It currently supports requests that contain +// a set of string parameters (key/value pairs), and a file to upload. + +#ifndef COMMON_WINDOWS_HTTP_UPLOAD_H__ +#define COMMON_WINDOWS_HTTP_UPLOAD_H__ + +#include +#include +#include +namespace google_airbag { + +using std::string; +using std::wstring; +using std::map; +using std::vector; + +class HTTPUpload { + public: + // Sends the given set of parameters, along with the contents of + // upload_file, as a multipart POST request to the given URL. + // file_part_name contains the name of the file part of the request + // (i.e. it corresponds to the name= attribute on an . + // 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 SendRequest(const wstring &url, + const map ¶meters, + const wstring &upload_file, + const wstring &file_part_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, an upload filename, and a file part name, + // generates a multipart request body string with these parameters + // and minidump contents. Returns true on success. + static bool GenerateRequestBody(const map ¶meters, + const wstring &upload_file, + const wstring &file_part_name, + const wstring &boundary, + string *request_body); + + // Fills the supplied vector with the contents of filename. + static void GetFileContents(const wstring &filename, vector *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 ¶meters); + + // No instances of this class should be created. + // Disallow all constructors, destructors, and operator=. + HTTPUpload(); + explicit HTTPUpload(const HTTPUpload &); + void operator=(const HTTPUpload &); + ~HTTPUpload(); +}; + +} // namespace google_airbag + +#endif // COMMON_WINDOWS_HTTP_UPLOAD_H__ diff --git a/src/tools/windows/dump_syms/pdb_source_line_writer.cc b/src/common/windows/pdb_source_line_writer.cc similarity index 87% rename from src/tools/windows/dump_syms/pdb_source_line_writer.cc rename to src/common/windows/pdb_source_line_writer.cc index 18b5d9ce..b6fe15fe 100644 --- a/src/tools/windows/dump_syms/pdb_source_line_writer.cc +++ b/src/common/windows/pdb_source_line_writer.cc @@ -30,7 +30,7 @@ #include #include #include -#include "pdb_source_line_writer.h" +#include "common/windows/pdb_source_line_writer.h" namespace google_airbag { @@ -40,7 +40,7 @@ PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) { PDBSourceLineWriter::~PDBSourceLineWriter() { } -bool PDBSourceLineWriter::Open(const wstring &pdb_file) { +bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) { Close(); if (FAILED(CoInitialize(NULL))) { @@ -55,9 +55,22 @@ bool PDBSourceLineWriter::Open(const wstring &pdb_file) { return false; } - if (FAILED(data_source->loadDataFromPdb(pdb_file.c_str()))) { - fprintf(stderr, "loadDataFromPdb failed\n"); - return false; + switch (format) { + case PDB_FILE: + if (FAILED(data_source->loadDataFromPdb(file.c_str()))) { + fprintf(stderr, "loadDataFromPdb failed\n"); + return false; + } + break; + case EXE_FILE: + if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) { + fprintf(stderr, "loadDataForExe failed\n"); + return false; + } + break; + default: + fprintf(stderr, "Unknown file format\n"); + return false; } if (FAILED(data_source->openSession(&session_))) { @@ -310,4 +323,25 @@ void PDBSourceLineWriter::Close() { session_.Release(); } +wstring PDBSourceLineWriter::GetModuleGUID() { + CComPtr global; + if (FAILED(session_->get_globalScope(&global))) { + return L""; + } + + GUID guid; + if (FAILED(global->get_guid(&guid))) { + return L""; + } + + wchar_t guid_buf[37]; + _snwprintf_s(guid_buf, sizeof(guid_buf)/sizeof(wchar_t), _TRUNCATE, + L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", + 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_buf; +} + } // namespace google_airbag diff --git a/src/tools/windows/dump_syms/pdb_source_line_writer.h b/src/common/windows/pdb_source_line_writer.h similarity index 81% rename from src/tools/windows/dump_syms/pdb_source_line_writer.h rename to src/common/windows/pdb_source_line_writer.h index c6c6563a..c4ce489f 100644 --- a/src/tools/windows/dump_syms/pdb_source_line_writer.h +++ b/src/common/windows/pdb_source_line_writer.h @@ -46,12 +46,24 @@ using std::wstring; class PDBSourceLineWriter { public: + enum FileFormat { + PDB_FILE, // a .pdb file containing debug symbols + EXE_FILE, // a .exe or .dll file + }; + explicit PDBSourceLineWriter(); ~PDBSourceLineWriter(); - // Opens the given pdb file. If there is already a pdb file open, - // it is automatically closed. Returns true on success. - bool Open(const wstring &pdb_file); + // Opens the given file. For executable files, the corresponding pdb + // file must be available; Open will be if it is not. + // If there is already a pdb file open, it is automatically closed. + // Returns true on success. + bool Open(const wstring &file, FileFormat format); + + // Locates the pdb file for the given executable (exe or dll) file, + // and opens it. If there is already a pdb file open, it is automatically + // closed. Returns true on success. + bool OpenExecutable(const wstring &exe_file); // Writes a map file from the current pdb file to the given file stream. // Returns true on success. @@ -60,6 +72,10 @@ class PDBSourceLineWriter { // Closes the current pdb file and its associated resources. void Close(); + // Returns the GUID for the module, as a string, + // e.g. "11111111-2222-3333-4444-555555555555". + wstring GetModuleGUID(); + private: // Outputs the line/address pairs for each line in the enumerator. // Returns true on success. diff --git a/src/tools/windows/dump_syms/dump_syms.cc b/src/tools/windows/dump_syms/dump_syms.cc index c2fa90c2..ca63c2c4 100644 --- a/src/tools/windows/dump_syms/dump_syms.cc +++ b/src/tools/windows/dump_syms/dump_syms.cc @@ -32,9 +32,10 @@ #include #include -#include "pdb_source_line_writer.h" +#include "common/windows/pdb_source_line_writer.h" using std::wstring; +using google_airbag::PDBSourceLineWriter; int main(int argc, char **argv) { if (argc < 2) { @@ -48,8 +49,8 @@ int main(int argc, char **argv) { return 1; } - google_airbag::PDBSourceLineWriter writer; - if (!writer.Open(wstring(filename))) { + PDBSourceLineWriter writer; + if (!writer.Open(wstring(filename), PDBSourceLineWriter::PDB_FILE)) { fprintf(stderr, "Open failed\n"); return 1; } diff --git a/src/tools/windows/dump_syms/dump_syms.vcproj b/src/tools/windows/dump_syms/dump_syms.vcproj index 2b42adea..19682d6e 100644 --- a/src/tools/windows/dump_syms/dump_syms.vcproj +++ b/src/tools/windows/dump_syms/dump_syms.vcproj @@ -1,206 +1,206 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tools/windows/symupload/symupload.cc b/src/tools/windows/symupload/symupload.cc new file mode 100644 index 00000000..7b530d0f --- /dev/null +++ b/src/tools/windows/symupload/symupload.cc @@ -0,0 +1,180 @@ +// 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. + +// Tool to upload an exe/dll and its associated symbols to an HTTP server. +// The PDB file is located automatically, using the path embedded in the +// executable. The upload is sent as a multipart/form-data POST request, +// with the following parameters: +// module: the name of the module, e.g. app.exe +// ver: the file version of the module, e.g. 1.2.3.4 +// guid: the GUID string embedded in the module pdb, +// e.g. 11111111-2222-3333-4444-555555555555 +// symbol_file: the airbag-format symbol file + +#include +#include +#include + +#include +#include +#include +#include + +#include "common/windows/pdb_source_line_writer.h" +#include "common/windows/http_upload.h" + +using std::string; +using std::wstring; +using std::vector; +using std::map; +using google_airbag::HTTPUpload; +using google_airbag::PDBSourceLineWriter; + +// Extracts the file version information for the given filename, +// as a string, for example, "1.2.3.4". Returns true on success. +static bool GetFileVersionString(const wchar_t *filename, wstring *version) { + DWORD handle; + DWORD version_size = GetFileVersionInfoSize(filename, &handle); + if (version_size < sizeof(VS_FIXEDFILEINFO)) { + return false; + } + + vector version_info(version_size); + if (!GetFileVersionInfo(filename, handle, version_size, &version_info[0])) { + return false; + } + + void *file_info_buffer = NULL; + unsigned int file_info_length; + if (!VerQueryValue(&version_info[0], L"\\", + &file_info_buffer, &file_info_length)) { + return false; + } + + // The maximum value of each version component is 65535 (0xffff), + // so the max length is 24, including the terminating null. + wchar_t ver_string[24]; + VS_FIXEDFILEINFO *file_info = + reinterpret_cast(file_info_buffer); + _snwprintf_s(ver_string, sizeof(ver_string) / sizeof(wchar_t), _TRUNCATE, + L"%d.%d.%d.%d", + file_info->dwFileVersionMS >> 16, + file_info->dwFileVersionMS & 0xffff, + file_info->dwFileVersionLS >> 16, + file_info->dwFileVersionLS & 0xffff); + *version = ver_string; + return true; +} + +// Creates a new temporary file and writes the symbol data from the given +// exe/dll file to it. Returns the path to the temp file in temp_file_path, +// and the unique identifier (GUID) for the pdb in module_guid. +static bool DumpSymbolsToTempFile(const wchar_t *exe_file, + wstring *temp_file_path, + wstring *module_guid) { + google_airbag::PDBSourceLineWriter writer; + if (!writer.Open(exe_file, PDBSourceLineWriter::EXE_FILE)) { + return false; + } + + wchar_t temp_path[_MAX_PATH]; + if (GetTempPath(_MAX_PATH, temp_path) == 0) { + return false; + } + + wchar_t temp_filename[_MAX_PATH]; + if (GetTempFileName(temp_path, L"sym", 0, temp_filename) == 0) { + return false; + } + + FILE *temp_file = NULL; + if (_wfopen_s(&temp_file, temp_filename, L"w") != 0) { + return false; + } + + bool success = writer.WriteMap(temp_file); + fclose(temp_file); + if (!success) { + _wunlink(temp_filename); + return false; + } + + *temp_file_path = temp_filename; + *module_guid = writer.GetModuleGUID(); + return true; +} + +// Returns the base name of a file, e.g. strips off the path. +static wstring GetBaseName(const wstring &filename) { + wstring base_name(filename); + size_t slash_pos = base_name.find_last_of(L"/\\"); + if (slash_pos != string::npos) { + base_name.erase(0, slash_pos + 1); + } + return base_name; +} + +int wmain(int argc, wchar_t *argv[]) { + if (argc < 3) { + wprintf(L"Usage: %s file.[exe|dll] \n", argv[0]); + return 0; + } + const wchar_t *module = argv[1], *url = argv[2]; + wstring module_basename = GetBaseName(module); + + wstring file_version; + if (!GetFileVersionString(module, &file_version)) { + fwprintf(stderr, L"Could not get file version for %s\n", module); + return 1; + } + + wstring symbol_file, module_guid; + if (!DumpSymbolsToTempFile(module, &symbol_file, &module_guid)) { + fwprintf(stderr, L"Could not get symbol data from %s\n", module); + return 1; + } + + map parameters; + parameters[L"module"] = module_basename; + parameters[L"version"] = file_version; + parameters[L"guid"] = module_guid; + + bool success = HTTPUpload::SendRequest(url, parameters, + symbol_file, L"symbol_file"); + _wunlink(symbol_file.c_str()); + + if (!success) { + fwprintf(stderr, L"Symbol file upload failed\n"); + return 1; + } + + wprintf(L"Uploaded symbols for %s/%s/%s\n", + module_basename.c_str(), file_version.c_str(), module_guid.c_str()); + return 0; +} diff --git a/src/tools/windows/symupload/symupload.vcproj b/src/tools/windows/symupload/symupload.vcproj new file mode 100755 index 00000000..63b731c1 --- /dev/null +++ b/src/tools/windows/symupload/symupload.vcproj @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +