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
This commit is contained in:
bryner 2006-10-16 17:27:03 +00:00
parent fc1c78e60e
commit 8b1645d8cd
11 changed files with 1046 additions and 470 deletions

View file

@ -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 <assert.h>
#include <windows.h>
#include <wininet.h>
#include <fstream>
#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<wstring, wstring> &parameters,
const wstring &dump_file_name) {
// TODO(bryner): support non-ASCII parameter names
if (!CheckParameters(parameters)) {
return false;
}
// Break up the URL and make sure we can handle it
wchar_t scheme[16], host[256], path[256];
URL_COMPONENTS components;
memset(&components, 0, sizeof(components));
components.dwStructSize = sizeof(components);
components.lpszScheme = scheme;
components.dwSchemeLength = sizeof(scheme);
components.lpszHostName = host;
components.dwHostNameLength = sizeof(host);
components.lpszUrlPath = path;
components.dwUrlPathLength = sizeof(path);
if (!InternetCrackUrl(url.c_str(), static_cast<DWORD>(url.size()),
0, &components)) {
return false;
}
if (wcscmp(scheme, L"http") != 0) {
return false;
}
AutoInternetHandle internet(InternetOpen(kUserAgent,
INTERNET_OPEN_TYPE_DIRECT,
NULL, // proxy name
NULL, // proxy bypass
0)); // flags
if (!internet.get()) {
return false;
}
AutoInternetHandle connection(InternetConnect(internet.get(),
host,
components.nPort,
NULL, // user name
NULL, // password
INTERNET_SERVICE_HTTP,
0, // flags
NULL)); // context
if (!connection.get()) {
return false;
}
AutoInternetHandle request(HttpOpenRequest(connection.get(),
L"POST",
path,
NULL, // version
NULL, // referer
NULL, // agent type
0, // flags
NULL)); // context
if (!request.get()) {
return false;
}
wstring boundary = GenerateMultipartBoundary();
wstring content_type_header = GenerateRequestHeader(boundary);
HttpAddRequestHeaders(request.get(),
content_type_header.c_str(),
-1, HTTP_ADDREQ_FLAG_ADD);
string request_body;
GenerateRequestBody(parameters, dump_file_name, boundary, &request_body);
// The explicit comparison to TRUE avoids a warning (C4800).
return (HttpSendRequest(request.get(), NULL, 0,
const_cast<char *>(request_body.data()),
static_cast<DWORD>(request_body.size())) == TRUE);
}
// static
wstring CrashReportSender::GenerateMultipartBoundary() {
// The boundary has 27 '-' characters followed by 16 hex digits
static const wchar_t kBoundaryPrefix[] = L"---------------------------";
static const int kBoundaryLength = 27 + 16 + 1;
// Generate some random numbers to fill out the boundary
int r0 = rand();
int r1 = rand();
wchar_t temp[kBoundaryLength];
swprintf_s(temp, kBoundaryLength, L"%s%08X%08X", kBoundaryPrefix, r0, r1);
return wstring(temp);
}
// static
wstring CrashReportSender::GenerateRequestHeader(const wstring &boundary) {
wstring header = L"Content-Type: multipart/form-data; boundary=";
header += boundary;
return header;
}
// static
bool CrashReportSender::GenerateRequestBody(
const map<wstring, wstring> &parameters,
const wstring &minidump_filename, const wstring &boundary,
string *request_body) {
vector<char> contents;
GetFileContents(minidump_filename, &contents);
if (contents.empty()) {
return false;
}
string boundary_str = WideToUTF8(boundary);
if (boundary_str.empty()) {
return false;
}
request_body->clear();
// Append each of the parameter pairs as a form-data part
for (map<wstring, wstring>::const_iterator pos = parameters.begin();
pos != parameters.end(); ++pos) {
request_body->append("--" + boundary_str + "\r\n");
request_body->append("Content-Disposition: form-data; name=\"" +
WideToUTF8(pos->first) + "\"\r\n\r\n" +
WideToUTF8(pos->second) + "\r\n");
}
// Now append the minidump file as a binary (octet-stream) part
string filename_utf8 = WideToUTF8(minidump_filename);
if (filename_utf8.empty()) {
return false;
}
request_body->append("--" + boundary_str + "\r\n");
request_body->append("Content-Disposition: form-data; "
"name=\"upload_file_minidump\"; "
"filename=\"" + filename_utf8 + "\"\r\n");
request_body->append("Content-Type: application/octet-stream\r\n");
request_body->append("\r\n");
request_body->append(&(contents[0]), contents.size());
request_body->append("\r\n");
request_body->append("--" + boundary_str + "--\r\n");
return true;
}
// static
void CrashReportSender::GetFileContents(const wstring &filename,
vector<char> *contents) {
ifstream file;
file.open(filename.c_str(), ios::binary);
if (file.is_open()) {
file.seekg(0, ios::end);
int length = file.tellg();
contents->resize(length);
file.seekg(0, ios::beg);
file.read(&((*contents)[0]), length);
file.close();
} else {
contents->clear();
}
}
// static
string CrashReportSender::WideToUTF8(const wstring &wide) {
if (wide.length() == 0) {
return string();
}
// compute the length of the buffer we'll need
int charcount = WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), -1,
NULL, 0, NULL, NULL);
if (charcount == 0) {
return string();
}
// convert
char *buf = new char[charcount];
WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), -1, buf, charcount,
NULL, NULL);
string result(buf);
delete[] buf;
return result;
}
// static
bool CrashReportSender::CheckParameters(
const map<wstring, wstring> &parameters) {
for (map<wstring, wstring>::const_iterator pos = parameters.begin();
pos != parameters.end(); ++pos) {
const wstring &str = pos->first;
if (str.size() == 0) {
return false; // disallow empty parameter names
}
for (unsigned int i = 0; i < str.size(); ++i) {
wchar_t c = str[i];
if (c < 32 || c == '"' || c > 127) {
return false;
}
}
}
return true;
return HTTPUpload::SendRequest(url, parameters, dump_file_name,
L"upload_file_minidump");
}
} // namespace google_airbag

View file

@ -40,14 +40,11 @@
#include <string>
#include <map>
#include <vector>
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<wstring, wstring> &parameters,
const wstring &minidump_filename,
const wstring &boundary,
string *request_body);
// Fills the supplied vector with the contents of filename.
static void GetFileContents(const wstring &filename, vector<char> *contents);
// Converts a UTF16 string to UTF8.
static string WideToUTF8(const wstring &wide);
// Checks that the given list of parameters has only printable
// ASCII characters in the parameter name, and does not contain
// any quote (") characters. Returns true if so.
static bool CheckParameters(const map<wstring, wstring> &parameters);
// No instances of this class should be created.
// Disallow all constructors, destructors, and operator=.
CrashReportSender();

View file

@ -276,6 +276,10 @@
RelativePath=".\crash_report_sender.cc"
>
</File>
<File
RelativePath="..\..\..\common\windows\http_upload.cc"
>
</File>
</Filter>
<Filter
Name="Header Files"
@ -286,6 +290,10 @@
RelativePath=".\crash_report_sender.h"
>
</File>
<File
RelativePath="..\..\..\common\windows\http_upload.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"

View file

@ -0,0 +1,271 @@
// 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 "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<wstring, wstring> &parameters,
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<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, 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<char *>(request_body.data()),
static_cast<DWORD>(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<wstring, wstring> &parameters,
const wstring &upload_file,
const wstring &file_part_name,
const wstring &boundary,
string *request_body) {
vector<char> 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<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 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<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 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<wstring, wstring> &parameters) {
for (map<wstring, wstring>::const_iterator pos = parameters.begin();
pos != parameters.end(); ++pos) {
const wstring &str = pos->first;
if (str.size() == 0) {
return false; // disallow empty parameter names
}
for (unsigned int i = 0; i < str.size(); ++i) {
wchar_t c = str[i];
if (c < 32 || c == '"' || c > 127) {
return false;
}
}
}
return true;
}
} // namespace google_airbag

View file

@ -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 <string>
#include <map>
#include <vector>
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 <input type="file">.
// 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<wstring, wstring> &parameters,
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<wstring, wstring> &parameters,
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<char> *contents);
// Converts a UTF16 string to UTF8.
static string WideToUTF8(const wstring &wide);
// Checks that the given list of parameters has only printable
// ASCII characters in the parameter name, and does not contain
// any quote (") characters. Returns true if so.
static bool CheckParameters(const map<wstring, wstring> &parameters);
// No instances of this class should be created.
// Disallow all constructors, destructors, and operator=.
HTTPUpload();
explicit HTTPUpload(const HTTPUpload &);
void operator=(const HTTPUpload &);
~HTTPUpload();
};
} // namespace google_airbag
#endif // COMMON_WINDOWS_HTTP_UPLOAD_H__

View file

@ -30,7 +30,7 @@
#include <stdio.h>
#include <atlbase.h>
#include <dia2.h>
#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,10 +55,23 @@ bool PDBSourceLineWriter::Open(const wstring &pdb_file) {
return false;
}
if (FAILED(data_source->loadDataFromPdb(pdb_file.c_str()))) {
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_))) {
fprintf(stderr, "openSession failed\n");
@ -310,4 +323,25 @@ void PDBSourceLineWriter::Close() {
session_.Release();
}
wstring PDBSourceLineWriter::GetModuleGUID() {
CComPtr<IDiaSymbol> 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

View file

@ -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.

View file

@ -32,9 +32,10 @@
#include <stdio.h>
#include <string>
#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;
}

View file

@ -39,7 +39,7 @@
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="&quot;$(VSInstallDir)\DIA SDK\include&quot;"
AdditionalIncludeDirectories="&quot;$(VSInstallDir)\DIA SDK\include&quot;;..\..\.."
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;"
MinimalRebuild="true"
BasicRuntimeChecks="3"
@ -114,7 +114,7 @@
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="&quot;$(VSInstallDir)\DIA SDK\include&quot;"
AdditionalIncludeDirectories="&quot;$(VSInstallDir)\DIA SDK\include&quot;;..\..\.."
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;"
RuntimeLibrary="2"
UsePrecompiledHeader="0"
@ -176,7 +176,7 @@
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath=".\pdb_source_line_writer.h"
RelativePath="..\..\..\common\windows\pdb_source_line_writer.h"
>
</File>
</Filter>
@ -196,7 +196,7 @@
>
</File>
<File
RelativePath=".\pdb_source_line_writer.cc"
RelativePath="..\..\..\common\windows\pdb_source_line_writer.cc"
>
</File>
</Filter>

View file

@ -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 <windows.h>
#include <wininet.h>
#include <dbghelp.h>
#include <cstdio>
#include <vector>
#include <string>
#include <map>
#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<char> 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<VS_FIXEDFILEINFO*>(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] <symbol upload URL>\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<wstring, wstring> 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;
}

View file

@ -0,0 +1,215 @@
<?xml version="1.0" encoding="UTF-8"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="symupload"
ProjectGUID="{E156ED87-9DE9-47C8-94EC-A5A9CDD65E18}"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="Debug"
IntermediateDirectory="Debug"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="&quot;$(VSInstallDir)\DIA SDK\include&quot;;..\..\.."
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;"
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="VCLinkerTool"
AdditionalDependencies="&quot;$(VSInstallDir)\DIA SDK\lib\diaguids.lib&quot; wininet.lib version.lib"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="Release"
IntermediateDirectory="Release"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="&quot;$(VSInstallDir)\DIA SDK\include&quot;;..\..\.."
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;"
RuntimeLibrary="2"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="&quot;$(VSInstallDir)\DIA SDK\lib\diaguids.lib&quot; wininet.lib version.lib"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath="..\..\..\common\windows\http_upload.h"
>
</File>
<File
RelativePath="..\..\..\common\windows\pdb_source_line_writer.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\..\..\common\windows\http_upload.cc"
>
</File>
<File
RelativePath="..\..\..\common\windows\pdb_source_line_writer.cc"
>
</File>
<File
RelativePath=".\symupload.cc"
>
</File>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>