From 8b1645d8cdb34035c0b132fe8b574bc5ee48fb62 Mon Sep 17 00:00:00 2001 From: bryner Date: Mon, 16 Oct 2006 17:27:03 +0000 Subject: [PATCH] Implement a tool to upload symbols on Windows, given an exe or dll file with debugging info. Refactor common code into HTTPUpload so that the multipart POST request code can be shared with CrashReportSender. #47 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@39 4c0a9323-5329-0410-9bdc-e9ce6186880e --- .../windows/sender/crash_report_sender.cc | 226 +--------- .../windows/sender/crash_report_sender.h | 30 -- .../windows/sender/crash_report_sender.vcproj | 8 + src/common/windows/http_upload.cc | 271 ++++++++++++ src/common/windows/http_upload.h | 101 +++++ .../windows}/pdb_source_line_writer.cc | 44 +- .../windows}/pdb_source_line_writer.h | 22 +- src/tools/windows/dump_syms/dump_syms.cc | 7 +- src/tools/windows/dump_syms/dump_syms.vcproj | 412 +++++++++--------- src/tools/windows/symupload/symupload.cc | 180 ++++++++ src/tools/windows/symupload/symupload.vcproj | 215 +++++++++ 11 files changed, 1046 insertions(+), 470 deletions(-) create mode 100644 src/common/windows/http_upload.cc create mode 100644 src/common/windows/http_upload.h rename src/{tools/windows/dump_syms => common/windows}/pdb_source_line_writer.cc (87%) rename src/{tools/windows/dump_syms => common/windows}/pdb_source_line_writer.h (81%) create mode 100644 src/tools/windows/symupload/symupload.cc create mode 100755 src/tools/windows/symupload/symupload.vcproj 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +