diff options
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.cc | 752 |
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 |