summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/tools/windows/converter/ms_symbol_server_converter.cc
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/tools/windows/converter/ms_symbol_server_converter.cc')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/tools/windows/converter/ms_symbol_server_converter.cc752
1 files changed, 752 insertions, 0 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/tools/windows/converter/ms_symbol_server_converter.cc b/toolkit/crashreporter/google-breakpad/src/tools/windows/converter/ms_symbol_server_converter.cc
new file mode 100644
index 0000000000..2b40faeeb5
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/tools/windows/converter/ms_symbol_server_converter.cc
@@ -0,0 +1,752 @@
+// Copyright (c) 2007, 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.
+
+// ms_symbol_server_converter.cc: Obtain symbol files from a Microsoft
+// symbol server, and convert them to Breakpad's dumped format.
+//
+// See ms_symbol_server_converter.h for documentation.
+//
+// Author: Mark Mentovai
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <pathcch.h>
+
+#include <cassert>
+#include <cstdio>
+
+#include "tools/windows/converter/ms_symbol_server_converter.h"
+#include "common/windows/pdb_source_line_writer.h"
+#include "common/windows/pe_source_line_writer.h"
+#include "common/windows/string_utils-inl.h"
+
+// SYMOPT_NO_PROMPTS is not defined in earlier platform SDKs. Define it
+// in that case, in the event that this code is used with a newer version
+// of DbgHelp at runtime that recognizes the option. The presence of this
+// bit in the symbol options should not harm earlier versions of DbgHelp.
+#ifndef SYMOPT_NO_PROMPTS
+#define SYMOPT_NO_PROMPTS 0x00080000
+#endif // SYMOPT_NO_PROMPTS
+
+namespace {
+
+std::wstring GetExeDirectory() {
+ wchar_t directory[MAX_PATH];
+
+ // Get path to this process exe.
+ DWORD result = GetModuleFileName(/*hModule=*/nullptr, directory, MAX_PATH);
+ if (result <= 0 || result == MAX_PATH) {
+ fprintf(stderr,
+ "GetExeDirectory: failed to get path to process exe.\n");
+ return L"";
+ }
+ HRESULT hr = PathCchRemoveFileSpec(directory, result + 1);
+ if (hr != S_OK) {
+ fprintf(stderr,
+ "GetExeDirectory: failed to remove basename from path '%ls'.\n",
+ directory);
+ return L"";
+ }
+
+ return std::wstring(directory);
+}
+
+} // namespace
+
+namespace google_breakpad {
+
+// Use sscanf_s if it is available, to quench the warning about scanf being
+// deprecated. Use scanf where sscanf_is not available. Note that the
+// parameters passed to sscanf and sscanf_s are only compatible as long as
+// fields of type c, C, s, S, and [ are not used.
+#if _MSC_VER >= 1400 // MSVC 2005/8
+#define SSCANF sscanf_s
+#else // _MSC_VER >= 1400
+#define SSCANF sscanf
+#endif // _MSC_VER >= 1400
+
+bool GUIDOrSignatureIdentifier::InitializeFromString(
+ const string &identifier) {
+ type_ = TYPE_NONE;
+
+ size_t length = identifier.length();
+
+ if (length > 32 && length <= 40) {
+ // GUID
+ if (SSCANF(identifier.c_str(),
+ "%08X%04hX%04hX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%X",
+ &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],
+ &age_) != 12) {
+ return false;
+ }
+
+ type_ = TYPE_GUID;
+ } else if (length > 8 && length <= 15) {
+ // Signature
+ if (SSCANF(identifier.c_str(), "%08X%x", &signature_, &age_) != 2) {
+ return false;
+ }
+
+ type_ = TYPE_SIGNATURE;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+#undef SSCANF
+
+MSSymbolServerConverter::MSSymbolServerConverter(
+ const string &local_cache, const vector<string> &symbol_servers)
+ : symbol_path_(),
+ fail_dns_(false),
+ fail_timeout_(false),
+ fail_not_found_(false) {
+ // Setting local_cache can be done without verifying that it exists because
+ // SymSrv will create it if it is missing - any creation failures will occur
+ // at that time, so there's nothing to check here, making it safe to
+ // assign this in the constructor.
+
+ assert(symbol_servers.size() > 0);
+
+#if !defined(NDEBUG)
+ // These are characters that are interpreted as having special meanings in
+ // symbol_path_.
+ const char kInvalidCharacters[] = "*;";
+ assert(local_cache.find_first_of(kInvalidCharacters) == string::npos);
+#endif // !defined(NDEBUG)
+
+ for (vector<string>::const_iterator symbol_server = symbol_servers.begin();
+ symbol_server != symbol_servers.end();
+ ++symbol_server) {
+ // The symbol path format is explained by
+ // http://msdn.microsoft.com/library/en-us/debug/base/using_symsrv.asp .
+ // "srv*" is the same as "symsrv*symsrv.dll*", which means that
+ // symsrv.dll is to be responsible for locating symbols. symsrv.dll
+ // interprets the rest of the string as a series of symbol stores separated
+ // by '*'. "srv*local_cache*symbol_server" means to check local_cache
+ // first for the symbol file, and if it is not found there, to check
+ // symbol_server. Symbol files found on the symbol server will be placed
+ // in the local cache, decompressed.
+ //
+ // Multiple specifications in this format may be presented, separated by
+ // semicolons.
+
+ assert((*symbol_server).find_first_of(kInvalidCharacters) == string::npos);
+ symbol_path_ += "srv*" + local_cache + "*" + *symbol_server + ";";
+ }
+
+ // Strip the trailing semicolon.
+ symbol_path_.erase(symbol_path_.length() - 1);
+}
+
+// A stack-based class that manages SymInitialize and SymCleanup calls.
+class AutoSymSrv {
+ public:
+ AutoSymSrv() : initialized_(false) {}
+
+ ~AutoSymSrv() {
+ if (!Cleanup()) {
+ // Print the error message here, because destructors have no return
+ // value.
+ fprintf(stderr, "~AutoSymSrv: SymCleanup: error %lu\n", GetLastError());
+ }
+ }
+
+ bool Initialize(HANDLE process, char *path, bool invade_process) {
+ process_ = process;
+
+ // TODO(nbilling): Figure out why dbghelp.dll is being loaded from
+ // system32/SysWOW64 before exe folder.
+
+ // Attempt to locate and load dbghelp.dll beside the process exe. This is
+ // somewhat of a workaround to loader delay load behavior that is occurring
+ // when we call into symsrv APIs. dbghelp.dll must be loaded from beside
+ // the process exe so that we are guaranteed to find symsrv.dll alongside
+ // dbghelp.dll (a security requirement of dbghelp.dll) and so that the
+ // symsrv.dll file that is loaded has a symsrv.yes file alongside it (a
+ // requirement of symsrv.dll when accessing Microsoft-owned symbol
+ // servers).
+ // 'static local' because we don't care about the value but we need the
+ // initialization to happen exactly once.
+ static HMODULE dbghelp_module = [] () -> HMODULE {
+ std::wstring exe_directory = GetExeDirectory();
+ if (exe_directory.empty()) {
+ return nullptr;
+ }
+ std::wstring dbghelp_path = exe_directory + L"\\dbghelp.dll";
+ return LoadLibrary(dbghelp_path.c_str());
+ }();
+ if (dbghelp_module == nullptr) {
+ fprintf(stderr,
+ "AutoSymSrv::Initialize: failed to load dbghelp.dll beside exe.");
+ return false;
+ }
+
+ initialized_ = SymInitialize(process, path, invade_process) == TRUE;
+ return initialized_;
+ }
+
+ bool Cleanup() {
+ if (initialized_) {
+ if (SymCleanup(process_)) {
+ initialized_ = false;
+ return true;
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ private:
+ HANDLE process_;
+ bool initialized_;
+};
+
+// A stack-based class that "owns" a pathname and deletes it when destroyed,
+// unless told not to by having its Release() method called. Early deletions
+// are supported by calling Delete().
+class AutoDeleter {
+ public:
+ explicit AutoDeleter(const string &path) : path_(path) {}
+
+ ~AutoDeleter() {
+ int error;
+ if ((error = Delete()) != 0) {
+ // Print the error message here, because destructors have no return
+ // value.
+ fprintf(stderr, "~AutoDeleter: Delete: error %d for %s\n",
+ error, path_.c_str());
+ }
+ }
+
+ int Delete() {
+ if (path_.empty())
+ return 0;
+
+ int error = remove(path_.c_str());
+ Release();
+ return error;
+ }
+
+ void Release() {
+ path_.clear();
+ }
+
+ private:
+ string path_;
+};
+
+MSSymbolServerConverter::LocateResult
+MSSymbolServerConverter::LocateFile(const string &debug_or_code_file,
+ const string &debug_or_code_id,
+ const string &version,
+ string *file_name) {
+ assert(file_name);
+ file_name->clear();
+
+ GUIDOrSignatureIdentifier identifier;
+ if (!identifier.InitializeFromString(debug_or_code_id)) {
+ fprintf(stderr,
+ "LocateFile: Unparseable identifier for %s %s %s\n",
+ debug_or_code_file.c_str(),
+ debug_or_code_id.c_str(),
+ version.c_str());
+ return LOCATE_FAILURE;
+ }
+
+ HANDLE process = GetCurrentProcess(); // CloseHandle is not needed.
+ AutoSymSrv symsrv;
+ if (!symsrv.Initialize(process,
+ const_cast<char *>(symbol_path_.c_str()),
+ false)) {
+ fprintf(stderr, "LocateFile: SymInitialize: error %lu for %s %s %s\n",
+ GetLastError(),
+ debug_or_code_file.c_str(),
+ debug_or_code_id.c_str(),
+ version.c_str());
+ return LOCATE_FAILURE;
+ }
+
+ if (!SymRegisterCallback64(process, SymCallback,
+ reinterpret_cast<ULONG64>(this))) {
+ fprintf(stderr,
+ "LocateFile: SymRegisterCallback64: error %lu for %s %s %s\n",
+ GetLastError(),
+ debug_or_code_file.c_str(),
+ debug_or_code_id.c_str(),
+ version.c_str());
+ return LOCATE_FAILURE;
+ }
+
+ // SYMOPT_DEBUG arranges for SymCallback to be called with additional
+ // debugging information. This is used to determine the nature of failures.
+ DWORD options = SymGetOptions() | SYMOPT_DEBUG | SYMOPT_NO_PROMPTS |
+ SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_SECURE;
+ SymSetOptions(options);
+
+ // SymCallback will set these as needed inisde the SymFindFileInPath call.
+ fail_dns_ = false;
+ fail_timeout_ = false;
+ fail_not_found_ = false;
+
+ // Do the lookup.
+ char path[MAX_PATH];
+ if (!SymFindFileInPath(
+ process, NULL,
+ const_cast<char *>(debug_or_code_file.c_str()),
+ const_cast<void *>(identifier.guid_or_signature_pointer()),
+ identifier.age(), 0,
+ identifier.type() == GUIDOrSignatureIdentifier::TYPE_GUID ?
+ SSRVOPT_GUIDPTR : SSRVOPT_DWORDPTR,
+ path, SymFindFileInPathCallback, this)) {
+ DWORD error = GetLastError();
+ if (error == ERROR_FILE_NOT_FOUND) {
+ // This can be returned for a number of reasons. Use the crumbs
+ // collected by SymCallback to determine which one is relevant.
+
+ // These errors are possibly transient.
+ if (fail_dns_ || fail_timeout_) {
+ return LOCATE_RETRY;
+ }
+
+ // This is an authoritiative file-not-found message.
+ if (fail_not_found_) {
+ fprintf(stderr,
+ "LocateFile: SymFindFileInPath: LOCATE_NOT_FOUND error "
+ "for %s %s %s\n",
+ debug_or_code_file.c_str(),
+ debug_or_code_id.c_str(),
+ version.c_str());
+ return LOCATE_NOT_FOUND;
+ }
+
+ // If the error is FILE_NOT_FOUND but none of the known error
+ // conditions are matched, fall through to LOCATE_FAILURE.
+ }
+
+ fprintf(stderr,
+ "LocateFile: SymFindFileInPath: error %lu for %s %s %s\n",
+ error,
+ debug_or_code_file.c_str(),
+ debug_or_code_id.c_str(),
+ version.c_str());
+ return LOCATE_FAILURE;
+ }
+
+ // Making sure path is null-terminated.
+ path[MAX_PATH - 1] = '\0';
+
+ // The AutoDeleter ensures that the file is only kept when returning
+ // LOCATE_SUCCESS.
+ AutoDeleter deleter(path);
+
+ // Do the cleanup here even though it will happen when symsrv goes out of
+ // scope, to allow it to influence the return value.
+ if (!symsrv.Cleanup()) {
+ fprintf(stderr, "LocateFile: SymCleanup: error %lu for %s %s %s\n",
+ GetLastError(),
+ debug_or_code_file.c_str(),
+ debug_or_code_id.c_str(),
+ version.c_str());
+ return LOCATE_FAILURE;
+ }
+
+ deleter.Release();
+
+ printf("Downloaded: %s\n", path);
+ *file_name = path;
+ return LOCATE_SUCCESS;
+}
+
+
+MSSymbolServerConverter::LocateResult
+MSSymbolServerConverter::LocatePEFile(const MissingSymbolInfo &missing,
+ string *pe_file) {
+ return LocateFile(missing.code_file, missing.code_identifier,
+ missing.version, pe_file);
+}
+
+MSSymbolServerConverter::LocateResult
+MSSymbolServerConverter::LocateSymbolFile(const MissingSymbolInfo &missing,
+ string *symbol_file) {
+ return LocateFile(missing.debug_file, missing.debug_identifier,
+ missing.version, symbol_file);
+}
+
+
+// static
+BOOL CALLBACK MSSymbolServerConverter::SymCallback(HANDLE process,
+ ULONG action,
+ ULONG64 data,
+ ULONG64 context) {
+ MSSymbolServerConverter *self =
+ reinterpret_cast<MSSymbolServerConverter *>(context);
+
+ switch (action) {
+ case CBA_EVENT: {
+ IMAGEHLP_CBA_EVENT *cba_event =
+ reinterpret_cast<IMAGEHLP_CBA_EVENT *>(data);
+
+ // Put the string into a string object to be able to use string::find
+ // for substring matching. This is important because the not-found
+ // message does not use the entire string but is appended to the URL
+ // that SymSrv attempted to retrieve.
+ string desc(cba_event->desc);
+
+ // desc_action maps strings (in desc) to boolean pointers that are to
+ // be set to true if the string matches.
+ struct desc_action {
+ const char *desc; // The substring to match.
+ bool *action; // On match, this pointer will be set to true.
+ };
+
+ static const desc_action desc_actions[] = {
+ // When a DNS error occurs, it could be indiciative of network
+ // problems.
+ { "SYMSRV: The server name or address could not be resolved\n",
+ &self->fail_dns_ },
+
+ // This message is produced if no connection is opened.
+ { "SYMSRV: A connection with the server could not be established\n",
+ &self->fail_timeout_ },
+
+ // This message is produced if a connection is established but the
+ // server fails to respond to the HTTP request.
+ { "SYMSRV: The operation timed out\n",
+ &self->fail_timeout_ },
+
+ // This message is produced when the requested file is not found,
+ // even if one or more of the above messages are also produced.
+ // It's trapped to distinguish between not-found and unknown-failure
+ // conditions. Note that this message will not be produced if a
+ // connection is established and the server begins to respond to the
+ // HTTP request but does not finish transmitting the file.
+ { " not found\n",
+ &self->fail_not_found_ }
+ };
+
+ for (int desc_action_index = 0;
+ desc_action_index <
+ static_cast<int>(sizeof(desc_actions) / sizeof(desc_action));
+ ++desc_action_index) {
+ if (desc.find(desc_actions[desc_action_index].desc) != string::npos) {
+ *(desc_actions[desc_action_index].action) = true;
+ break;
+ }
+ }
+
+ break;
+ }
+ }
+
+ // This function is a mere fly on the wall. Treat everything as unhandled.
+ return FALSE;
+}
+
+// static
+BOOL CALLBACK MSSymbolServerConverter::SymFindFileInPathCallback(
+ const char *filename, void *context) {
+ // FALSE ends the search, indicating that the located symbol file is
+ // satisfactory.
+ return FALSE;
+}
+
+MSSymbolServerConverter::LocateResult
+MSSymbolServerConverter::LocateAndConvertSymbolFile(
+ const MissingSymbolInfo &missing,
+ bool keep_symbol_file,
+ bool keep_pe_file,
+ string *converted_symbol_file,
+ string *symbol_file,
+ string *out_pe_file) {
+ assert(converted_symbol_file);
+ converted_symbol_file->clear();
+ if (symbol_file) {
+ symbol_file->clear();
+ }
+
+ string pdb_file;
+ LocateResult result = LocateSymbolFile(missing, &pdb_file);
+ if (result != LOCATE_SUCCESS) {
+ fprintf(stderr, "Fallback to PE-only symbol generation for: %s\n",
+ missing.debug_file.c_str());
+ return LocateAndConvertPEFile(missing, keep_pe_file, converted_symbol_file,
+ out_pe_file);
+ }
+
+ if (symbol_file && keep_symbol_file) {
+ *symbol_file = pdb_file;
+ }
+
+ // The conversion of a symbol file for a Windows 64-bit module requires
+ // loading of the executable file. If there is no executable file, convert
+ // using only the PDB file. Without an executable file, the conversion will
+ // fail for 64-bit modules but it should succeed for 32-bit modules.
+ string pe_file;
+ result = LocatePEFile(missing, &pe_file);
+ if (result != LOCATE_SUCCESS) {
+ fprintf(stderr, "WARNING: Could not download: %s\n", pe_file.c_str());
+ }
+
+ if (out_pe_file && keep_pe_file) {
+ *out_pe_file = pe_file;
+ }
+
+ // Conversion may fail because the file is corrupt. If a broken file is
+ // kept in the local cache, LocateSymbolFile will not hit the network again
+ // to attempt to locate it. To guard against problems like this, the
+ // symbol file in the local cache will be removed if conversion fails.
+ AutoDeleter pdb_deleter(pdb_file);
+ AutoDeleter pe_deleter(pe_file);
+
+ // Be sure that it's a .pdb file, since we'll be replacing .pdb with .sym
+ // for the converted file's name.
+ string pdb_extension = pdb_file.substr(pdb_file.length() - 4);
+ // strcasecmp is called _stricmp here.
+ if (_stricmp(pdb_extension.c_str(), ".pdb") != 0) {
+ fprintf(stderr, "LocateAndConvertSymbolFile: "
+ "no .pdb extension for %s %s %s %s\n",
+ missing.debug_file.c_str(),
+ missing.debug_identifier.c_str(),
+ missing.version.c_str(),
+ pdb_file.c_str());
+ return LOCATE_FAILURE;
+ }
+
+ PDBSourceLineWriter writer;
+ wstring pe_file_w;
+ if (!WindowsStringUtils::safe_mbstowcs(pe_file, &pe_file_w)) {
+ fprintf(stderr,
+ "LocateAndConvertSymbolFile: "
+ "WindowsStringUtils::safe_mbstowcs failed for %s\n",
+ pe_file.c_str());
+ return LOCATE_FAILURE;
+ }
+ wstring pdb_file_w;
+ if (!WindowsStringUtils::safe_mbstowcs(pdb_file, &pdb_file_w)) {
+ fprintf(stderr,
+ "LocateAndConvertSymbolFile: "
+ "WindowsStringUtils::safe_mbstowcs failed for %ws\n",
+ pdb_file_w.c_str());
+ return LOCATE_FAILURE;
+ }
+ if (!writer.Open(pdb_file_w, PDBSourceLineWriter::PDB_FILE)) {
+ fprintf(stderr,
+ "ERROR: PDBSourceLineWriter::Open failed for %s %s %s %ws\n",
+ missing.debug_file.c_str(), missing.debug_identifier.c_str(),
+ missing.version.c_str(), pdb_file_w.c_str());
+ return LOCATE_FAILURE;
+ }
+ if (!writer.SetCodeFile(pe_file_w)) {
+ fprintf(stderr,
+ "ERROR: PDBSourceLineWriter::SetCodeFile failed for %s %s %s %ws\n",
+ missing.debug_file.c_str(), missing.debug_identifier.c_str(),
+ missing.version.c_str(), pe_file_w.c_str());
+ return LOCATE_FAILURE;
+ }
+
+ *converted_symbol_file = pdb_file.substr(0, pdb_file.length() - 4) + ".sym";
+
+ FILE *converted_output = NULL;
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ errno_t err;
+ if ((err = fopen_s(&converted_output, converted_symbol_file->c_str(), "w"))
+ != 0) {
+#else // _MSC_VER >= 1400
+ // fopen_s and errno_t were introduced in MSVC8. Use fopen for earlier
+ // environments. Don't use fopen with MSVC8 and later, because it's
+ // deprecated. fopen does not provide reliable error codes, so just use
+ // -1 in the event of a failure.
+ int err;
+ if (!(converted_output = fopen(converted_symbol_file->c_str(), "w"))) {
+ err = -1;
+#endif // _MSC_VER >= 1400
+ fprintf(stderr, "LocateAndConvertSymbolFile: "
+ "fopen_s: error %d for %s %s %s %s\n",
+ err,
+ missing.debug_file.c_str(),
+ missing.debug_identifier.c_str(),
+ missing.version.c_str(),
+ converted_symbol_file->c_str());
+ return LOCATE_FAILURE;
+ }
+
+ AutoDeleter sym_deleter(*converted_symbol_file);
+
+ bool success = writer.WriteSymbols(converted_output);
+ fclose(converted_output);
+
+ if (!success) {
+ fprintf(stderr, "LocateAndConvertSymbolFile: "
+ "PDBSourceLineWriter::WriteMap failed for %s %s %s %s\n",
+ missing.debug_file.c_str(),
+ missing.debug_identifier.c_str(),
+ missing.version.c_str(),
+ pdb_file.c_str());
+ return LOCATE_FAILURE;
+ }
+
+ if (keep_symbol_file) {
+ pdb_deleter.Release();
+ }
+
+ if (keep_pe_file) {
+ pe_deleter.Release();
+ }
+
+ sym_deleter.Release();
+
+ return LOCATE_SUCCESS;
+}
+
+MSSymbolServerConverter::LocateResult
+MSSymbolServerConverter::LocateAndConvertPEFile(
+ const MissingSymbolInfo &missing,
+ bool keep_pe_file,
+ string *converted_symbol_file,
+ string *out_pe_file) {
+ assert(converted_symbol_file);
+ converted_symbol_file->clear();
+
+ string pe_file;
+ MSSymbolServerConverter::LocateResult result = LocatePEFile(missing,
+ &pe_file);
+ if (result != LOCATE_SUCCESS) {
+ fprintf(stderr, "WARNING: Could not download: %s\n", pe_file.c_str());
+ return result;
+ }
+
+ if (out_pe_file && keep_pe_file) {
+ *out_pe_file = pe_file;
+ }
+
+ // Conversion may fail because the file is corrupt. If a broken file is
+ // kept in the local cache, LocatePEFile will not hit the network again
+ // to attempt to locate it. To guard against problems like this, the
+ // PE file in the local cache will be removed if conversion fails.
+ AutoDeleter pe_deleter(pe_file);
+
+ // Be sure that it's a .exe or .dll file, since we'll be replacing extension
+ // with .sym for the converted file's name.
+ string pe_extension = pe_file.substr(pe_file.length() - 4);
+ // strcasecmp is called _stricmp here.
+ if (_stricmp(pe_extension.c_str(), ".exe") != 0 &&
+ _stricmp(pe_extension.c_str(), ".dll") != 0) {
+ fprintf(stderr, "LocateAndConvertPEFile: "
+ "no .dll/.exe extension for %s %s %s %s\n",
+ missing.debug_file.c_str(),
+ missing.debug_identifier.c_str(),
+ missing.version.c_str(),
+ pe_file.c_str());
+ return LOCATE_FAILURE;
+ }
+
+ *converted_symbol_file = pe_file.substr(0, pe_file.length() - 4) + ".sym";
+
+ FILE *converted_output = NULL;
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ errno_t err;
+ if ((err = fopen_s(&converted_output, converted_symbol_file->c_str(), "w"))
+ != 0) {
+#else // _MSC_VER >= 1400
+ // fopen_s and errno_t were introduced in MSVC8. Use fopen for earlier
+ // environments. Don't use fopen with MSVC8 and later, because it's
+ // deprecated. fopen does not provide reliable error codes, so just use
+ // -1 in the event of a failure.
+ int err;
+ if (!(converted_output = fopen(converted_symbol_file->c_str(), "w"))) {
+ err = -1;
+#endif // _MSC_VER >= 1400
+ fprintf(stderr, "LocateAndConvertPEFile: "
+ "fopen_s: error %d for %s %s %s %s\n",
+ err,
+ missing.debug_file.c_str(),
+ missing.debug_identifier.c_str(),
+ missing.version.c_str(),
+ converted_symbol_file->c_str());
+ return LOCATE_FAILURE;
+ }
+ AutoDeleter sym_deleter(*converted_symbol_file);
+
+ wstring pe_file_w;
+ if (!WindowsStringUtils::safe_mbstowcs(pe_file, &pe_file_w)) {
+ fprintf(stderr,
+ "LocateAndConvertPEFile: "
+ "WindowsStringUtils::safe_mbstowcs failed for %s\n",
+ pe_file.c_str());
+ return LOCATE_FAILURE;
+ }
+ PESourceLineWriter writer(pe_file_w);
+ PDBModuleInfo module_info;
+ if (!writer.GetModuleInfo(&module_info)) {
+ fprintf(stderr, "LocateAndConvertPEFile: "
+ "PESourceLineWriter::GetModuleInfo failed for %s %s %s %s\n",
+ missing.debug_file.c_str(),
+ missing.debug_identifier.c_str(),
+ missing.version.c_str(),
+ pe_file.c_str());
+ return LOCATE_FAILURE;
+ }
+ if (module_info.cpu.compare(L"x86_64") != 0) {
+ // This module is not x64 so we cannot generate Breakpad symbols from the
+ // PE alone. Don't delete PE-- no need to retry download.
+ pe_deleter.Release();
+ return LOCATE_FAILURE;
+ }
+
+ bool success = writer.WriteSymbols(converted_output);
+ fclose(converted_output);
+
+ if (!success) {
+ fprintf(stderr, "LocateAndConvertPEFile: "
+ "PESourceLineWriter::WriteMap failed for %s %s %s %s\n",
+ missing.debug_file.c_str(),
+ missing.debug_identifier.c_str(),
+ missing.version.c_str(),
+ pe_file.c_str());
+ return LOCATE_FAILURE;
+ }
+
+ if (keep_pe_file) {
+ pe_deleter.Release();
+ }
+
+ sym_deleter.Release();
+
+ return LOCATE_SUCCESS;
+}
+
+} // namespace google_breakpad