aboutsummaryrefslogtreecommitdiff
path: root/src/common/windows
diff options
context:
space:
mode:
authorbryner <bryner@4c0a9323-5329-0410-9bdc-e9ce6186880e>2006-10-16 17:27:03 +0000
committerbryner <bryner@4c0a9323-5329-0410-9bdc-e9ce6186880e>2006-10-16 17:27:03 +0000
commit8b1645d8cdb34035c0b132fe8b574bc5ee48fb62 (patch)
treeb161860b45cce80cfbe75806f1d9d9cb2aa02fa8 /src/common/windows
parentHandle frame pointer omission (#21), part 3: SourceLineResolver and PDBSource... (diff)
downloadbreakpad-8b1645d8cdb34035c0b132fe8b574bc5ee48fb62.tar.xz
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
Diffstat (limited to 'src/common/windows')
-rw-r--r--src/common/windows/http_upload.cc271
-rw-r--r--src/common/windows/http_upload.h101
-rw-r--r--src/common/windows/pdb_source_line_writer.cc347
-rw-r--r--src/common/windows/pdb_source_line_writer.h112
4 files changed, 831 insertions, 0 deletions
diff --git a/src/common/windows/http_upload.cc b/src/common/windows/http_upload.cc
new file mode 100644
index 00000000..52bf46b3
--- /dev/null
+++ b/src/common/windows/http_upload.cc
@@ -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
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 <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__
diff --git a/src/common/windows/pdb_source_line_writer.cc b/src/common/windows/pdb_source_line_writer.cc
new file mode 100644
index 00000000..b6fe15fe
--- /dev/null
+++ b/src/common/windows/pdb_source_line_writer.cc
@@ -0,0 +1,347 @@
+// 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 <stdio.h>
+#include <atlbase.h>
+#include <dia2.h>
+#include "common/windows/pdb_source_line_writer.h"
+
+namespace google_airbag {
+
+PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) {
+}
+
+PDBSourceLineWriter::~PDBSourceLineWriter() {
+}
+
+bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) {
+ Close();
+
+ if (FAILED(CoInitialize(NULL))) {
+ fprintf(stderr, "CoInitialize failed\n");
+ return false;
+ }
+
+ CComPtr<IDiaDataSource> data_source;
+ if (FAILED(data_source.CoCreateInstance(CLSID_DiaSource))) {
+ fprintf(stderr, "CoCreateInstance CLSID_DiaSource failed "
+ "(msdia80.dll unregistered?)\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_))) {
+ fprintf(stderr, "openSession failed\n");
+ }
+
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) {
+ // The line number format is:
+ // <rva> <line number> <source file id>
+ CComPtr<IDiaLineNumber> line;
+ ULONG count;
+
+ while (SUCCEEDED(lines->Next(1, &line, &count)) && count == 1) {
+ DWORD rva;
+ if (FAILED(line->get_relativeVirtualAddress(&rva))) {
+ fprintf(stderr, "failed to get line rva\n");
+ return false;
+ }
+
+ DWORD length;
+ if (FAILED(line->get_length(&length))) {
+ fprintf(stderr, "failed to get line code length\n");
+ return false;
+ }
+
+ DWORD source_id;
+ if (FAILED(line->get_sourceFileId(&source_id))) {
+ fprintf(stderr, "failed to get line source file id\n");
+ return false;
+ }
+
+ DWORD line_num;
+ if (FAILED(line->get_lineNumber(&line_num))) {
+ fprintf(stderr, "failed to get line number\n");
+ return false;
+ }
+
+ fprintf(output_, "%x %x %d %d\n", rva, length, line_num, source_id);
+ line.Release();
+ }
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function) {
+ // The function format is:
+ // FUNC <address> <function>
+ CComBSTR name;
+ if (FAILED(function->get_name(&name))) {
+ fprintf(stderr, "failed to get function name\n");
+ return false;
+ }
+ if (name.Length() == 0) {
+ fprintf(stderr, "empty function name\n");
+ return false;
+ }
+
+ ULONGLONG length;
+ if (FAILED(function->get_length(&length))) {
+ fprintf(stderr, "failed to get function length\n");
+ return false;
+ }
+
+ DWORD rva;
+ if (FAILED(function->get_relativeVirtualAddress(&rva))) {
+ fprintf(stderr, "couldn't get rva\n");
+ return false;
+ }
+
+ CComPtr<IDiaEnumLineNumbers> lines;
+ if (FAILED(session_->findLinesByRVA(rva, DWORD(length), &lines))) {
+ return false;
+ }
+
+ fwprintf(output_, L"FUNC %x %llx %s\n", rva, length, name);
+ if (!PrintLines(lines)) {
+ return false;
+ }
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintSourceFiles() {
+ CComPtr<IDiaSymbol> global;
+ if (FAILED(session_->get_globalScope(&global))) {
+ fprintf(stderr, "get_globalScope failed\n");
+ return false;
+ }
+
+ CComPtr<IDiaEnumSymbols> compilands;
+ if (FAILED(global->findChildren(SymTagCompiland, NULL,
+ nsNone, &compilands))) {
+ fprintf(stderr, "findChildren failed\n");
+ return false;
+ }
+
+ CComPtr<IDiaSymbol> compiland;
+ ULONG count;
+ while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) {
+ CComPtr<IDiaEnumSourceFiles> source_files;
+ if (FAILED(session_->findFile(compiland, NULL, nsNone, &source_files))) {
+ return false;
+ }
+ CComPtr<IDiaSourceFile> file;
+ while (SUCCEEDED(source_files->Next(1, &file, &count)) && count == 1) {
+ DWORD file_id;
+ if (FAILED(file->get_uniqueId(&file_id))) {
+ return false;
+ }
+
+ CComBSTR file_name;
+ if (FAILED(file->get_fileName(&file_name))) {
+ return false;
+ }
+
+ fwprintf(output_, L"FILE %d %s\n", file_id, file_name);
+ file.Release();
+ }
+ compiland.Release();
+ }
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintFunctions() {
+ CComPtr<IDiaEnumSymbolsByAddr> symbols;
+ if (FAILED(session_->getSymbolsByAddr(&symbols))) {
+ fprintf(stderr, "failed to get symbol enumerator\n");
+ return false;
+ }
+
+ CComPtr<IDiaSymbol> symbol;
+ if (FAILED(symbols->symbolByAddr(1, 0, &symbol))) {
+ fprintf(stderr, "failed to enumerate symbols\n");
+ return false;
+ }
+
+ DWORD rva_last = 0;
+ if (FAILED(symbol->get_relativeVirtualAddress(&rva_last))) {
+ fprintf(stderr, "failed to get symbol rva\n");
+ return false;
+ }
+
+ ULONG count;
+ do {
+ DWORD tag;
+ if (FAILED(symbol->get_symTag(&tag))) {
+ fprintf(stderr, "failed to get symbol tag\n");
+ return false;
+ }
+ if (tag == SymTagFunction) {
+ if (!PrintFunction(symbol)) {
+ return false;
+ }
+ }
+ symbol.Release();
+ } while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1);
+
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintFrameData() {
+ // It would be nice if it were possible to output frame data alongside the
+ // associated function, as is done with line numbers, but the DIA API
+ // doesn't make it possible to get the frame data in that way.
+
+ CComPtr<IDiaEnumTables> tables;
+ if (FAILED(session_->getEnumTables(&tables)))
+ return false;
+
+ // Pick up the first table that supports IDiaEnumFrameData.
+ CComPtr<IDiaEnumFrameData> frame_data_enum;
+ CComPtr<IDiaTable> table;
+ ULONG count;
+ while (!frame_data_enum &&
+ SUCCEEDED(tables->Next(1, &table, &count)) &&
+ count == 1) {
+ table->QueryInterface(_uuidof(IDiaEnumFrameData),
+ reinterpret_cast<void**>(&frame_data_enum));
+ table.Release();
+ }
+ if (!frame_data_enum)
+ return false;
+
+ CComPtr<IDiaFrameData> frame_data;
+ while (SUCCEEDED(frame_data_enum->Next(1, &frame_data, &count)) &&
+ count == 1) {
+ DWORD type;
+ if (FAILED(frame_data->get_type(&type)))
+ return false;
+
+ DWORD rva;
+ if (FAILED(frame_data->get_relativeVirtualAddress(&rva)))
+ return false;
+
+ DWORD code_size;
+ if (FAILED(frame_data->get_lengthBlock(&code_size)))
+ return false;
+
+ DWORD prolog_size;
+ if (FAILED(frame_data->get_lengthProlog(&prolog_size)))
+ return false;
+
+ // epliog_size is always 0.
+ DWORD epilog_size = 0;
+
+ DWORD parameter_size;
+ if (FAILED(frame_data->get_lengthParams(&parameter_size)))
+ return false;
+
+ DWORD saved_register_size;
+ if (FAILED(frame_data->get_lengthSavedRegisters(&saved_register_size)))
+ return false;
+
+ DWORD local_size;
+ if (FAILED(frame_data->get_lengthLocals(&local_size)))
+ return false;
+
+ DWORD max_stack_size;
+ if (FAILED(frame_data->get_maxStack(&max_stack_size)))
+ return false;
+
+ BSTR program_string;
+ if (FAILED(frame_data->get_program(&program_string)))
+ return false;
+
+ fprintf(output_, "STACK WIN %x %x %x %x %x %x %x %x %x %ws\n",
+ type, rva, code_size, prolog_size, epilog_size,
+ parameter_size, saved_register_size, local_size, max_stack_size,
+ program_string);
+
+ frame_data.Release();
+ }
+
+ return true;
+}
+
+bool PDBSourceLineWriter::WriteMap(FILE *map_file) {
+ bool ret = false;
+ output_ = map_file;
+ if (PrintSourceFiles() && PrintFunctions() && PrintFrameData()) {
+ ret = true;
+ }
+
+ output_ = NULL;
+ return ret;
+}
+
+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
diff --git a/src/common/windows/pdb_source_line_writer.h b/src/common/windows/pdb_source_line_writer.h
new file mode 100644
index 00000000..c4ce489f
--- /dev/null
+++ b/src/common/windows/pdb_source_line_writer.h
@@ -0,0 +1,112 @@
+// 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.
+
+// PDBSourceLineWriter uses a pdb file produced by Visual C++ to output
+// a line/address map for use with SourceLineResolver.
+
+#ifndef _PDB_SOURCE_LINE_WRITER_H__
+#define _PDB_SOURCE_LINE_WRITER_H__
+
+#include <string>
+#include <atlcomcli.h>
+
+struct IDiaEnumLineNumbers;
+struct IDiaSession;
+struct IDiaSymbol;
+
+namespace google_airbag {
+
+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 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.
+ bool WriteMap(FILE *map_file);
+
+ // 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.
+ bool PrintLines(IDiaEnumLineNumbers *lines);
+
+ // Outputs a function address and name, followed by its source line list.
+ // Returns true on success.
+ bool PrintFunction(IDiaSymbol *function);
+
+ // Outputs all functions as described above. Returns true on success.
+ bool PrintFunctions();
+
+ // Outputs all of the source files in the session's pdb file.
+ // Returns true on success.
+ bool PrintSourceFiles();
+
+ // Outputs all of the frame information necessary to construct stack
+ // backtraces in the absence of frame pointers. Returns true on success.
+ bool PrintFrameData();
+
+ // The session for the currently-open pdb file.
+ CComPtr<IDiaSession> session_;
+
+ // The current output file for this WriteMap invocation.
+ FILE *output_;
+
+ // Disallow copy ctor and operator=
+ PDBSourceLineWriter(const PDBSourceLineWriter&);
+ void operator=(const PDBSourceLineWriter&);
+};
+
+} // namespace google_airbag
+
+#endif // _PDB_SOURCE_LINE_WRITER_H__