mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-06-03 07:27:10 +02:00
Move dead submodules in-tree
Signed-off-by: swurl <swurl@swurl.xyz>
This commit is contained in:
parent
c0cceff365
commit
6c655321e6
4081 changed files with 1185566 additions and 45 deletions
908
externals/breakpad/src/tools/windows/converter_exe/converter.cc
vendored
Normal file
908
externals/breakpad/src/tools/windows/converter_exe/converter.cc
vendored
Normal file
|
|
@ -0,0 +1,908 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
#pragma comment(lib, "winhttp.lib")
|
||||
#pragma comment(lib, "wininet.lib")
|
||||
#pragma comment(lib, "diaguids.lib")
|
||||
#pragma comment(lib, "imagehlp.lib")
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/windows/http_upload.h"
|
||||
#include "common/windows/string_utils-inl.h"
|
||||
#include "common/windows/sym_upload_v2_protocol.h"
|
||||
#include "tools/windows/converter/ms_symbol_server_converter.h"
|
||||
#include "tools/windows/converter_exe/escaping.h"
|
||||
#include "tools/windows/converter_exe/http_download.h"
|
||||
#include "tools/windows/converter_exe/tokenizer.h"
|
||||
|
||||
using strings::WebSafeBase64Unescape;
|
||||
using strings::WebSafeBase64Escape;
|
||||
|
||||
namespace {
|
||||
|
||||
using std::map;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::wstring;
|
||||
using crash::HTTPDownload;
|
||||
using crash::Tokenizer;
|
||||
using google_breakpad::HTTPUpload;
|
||||
using google_breakpad::MissingSymbolInfo;
|
||||
using google_breakpad::MSSymbolServerConverter;
|
||||
using google_breakpad::WindowsStringUtils;
|
||||
|
||||
const char* kMissingStringDelimiters = "|";
|
||||
const char* kLocalCachePath = "c:\\symbols";
|
||||
const char* kNoExeMSSSServer = "http://msdl.microsoft.com/download/symbols/";
|
||||
const wchar_t* kSymbolUploadTypeBreakpad = L"BREAKPAD";
|
||||
const wchar_t* kSymbolUploadTypePE = L"PE";
|
||||
const wchar_t* kSymbolUploadTypePDB = L"PDB";
|
||||
const wchar_t* kConverterProductName = L"WinSymConv";
|
||||
|
||||
// Windows stdio doesn't do line buffering. Use this function to flush after
|
||||
// writing to stdout and stderr so that a log will be available if the
|
||||
// converter crashes.
|
||||
static int FprintfFlush(FILE* file, const char* format, ...) {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
int retval = vfprintf(file, format, arguments);
|
||||
va_end(arguments);
|
||||
fflush(file);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static string CurrentDateAndTime() {
|
||||
const string kUnknownDateAndTime = R"(????-??-?? ??:??:??)";
|
||||
|
||||
time_t current_time;
|
||||
time(¤t_time);
|
||||
|
||||
// localtime_s is safer but is only available in MSVC8. Use localtime
|
||||
// in earlier environments.
|
||||
struct tm* time_pointer;
|
||||
#if _MSC_VER >= 1400 // MSVC 2005/8
|
||||
struct tm time_struct;
|
||||
time_pointer =& time_struct;
|
||||
if (localtime_s(time_pointer,& current_time) != 0) {
|
||||
return kUnknownDateAndTime;
|
||||
}
|
||||
#else // _MSC_VER >= 1400
|
||||
time_pointer = localtime(¤t_time);
|
||||
if (!time_pointer) {
|
||||
return kUnknownDateAndTime;
|
||||
}
|
||||
#endif // _MSC_VER >= 1400
|
||||
|
||||
char buffer[256];
|
||||
if (!strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", time_pointer)) {
|
||||
return kUnknownDateAndTime;
|
||||
}
|
||||
|
||||
return string(buffer);
|
||||
}
|
||||
|
||||
// ParseMissingString turns |missing_string| into a MissingSymbolInfo
|
||||
// structure. It returns true on success, and false if no such conversion
|
||||
// is possible.
|
||||
static bool ParseMissingString(const string& missing_string,
|
||||
MissingSymbolInfo* missing_info) {
|
||||
assert(missing_info);
|
||||
|
||||
vector<string> tokens;
|
||||
Tokenizer::Tokenize(kMissingStringDelimiters, missing_string,& tokens);
|
||||
if (tokens.size() != 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
missing_info->debug_file = tokens[0];
|
||||
missing_info->debug_identifier = tokens[1];
|
||||
missing_info->version = tokens[2];
|
||||
missing_info->code_file = tokens[3];
|
||||
missing_info->code_identifier = tokens[4];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// StringMapToWStringMap takes each element in a map that associates
|
||||
// (narrow) strings to strings and converts the keys and values to wstrings.
|
||||
// Returns true on success and false on failure, printing an error message.
|
||||
static bool StringMapToWStringMap(const map<string, string>& smap,
|
||||
map<wstring, wstring>* wsmap) {
|
||||
assert(wsmap);
|
||||
wsmap->clear();
|
||||
|
||||
for (map<string, string>::const_iterator iterator = smap.begin();
|
||||
iterator != smap.end();
|
||||
++iterator) {
|
||||
wstring key;
|
||||
if (!WindowsStringUtils::safe_mbstowcs(iterator->first,& key)) {
|
||||
FprintfFlush(stderr,
|
||||
"StringMapToWStringMap: safe_mbstowcs failed for key %s\n",
|
||||
iterator->first.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
wstring value;
|
||||
if (!WindowsStringUtils::safe_mbstowcs(iterator->second,& value)) {
|
||||
FprintfFlush(stderr, "StringMapToWStringMap: safe_mbstowcs failed "
|
||||
"for value %s\n",
|
||||
iterator->second.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
wsmap->insert(make_pair(key, value));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// MissingSymbolInfoToParameters turns a MissingSymbolInfo structure into a
|
||||
// map of parameters suitable for passing to HTTPDownload or HTTPUpload.
|
||||
// Returns true on success and false on failure, printing an error message.
|
||||
static bool MissingSymbolInfoToParameters(const MissingSymbolInfo& missing_info,
|
||||
map<wstring, wstring>* wparameters) {
|
||||
assert(wparameters);
|
||||
|
||||
map<string, string> parameters;
|
||||
string encoded_param;
|
||||
// Indicate the params are encoded.
|
||||
parameters["encoded"] = "true"; // The string value here does not matter.
|
||||
|
||||
WebSafeBase64Escape(missing_info.code_file,& encoded_param);
|
||||
parameters["code_file"] = encoded_param;
|
||||
|
||||
WebSafeBase64Escape(missing_info.code_identifier,& encoded_param);
|
||||
parameters["code_identifier"] = encoded_param;
|
||||
|
||||
WebSafeBase64Escape(missing_info.debug_file,& encoded_param);
|
||||
parameters["debug_file"] = encoded_param;
|
||||
|
||||
WebSafeBase64Escape(missing_info.debug_identifier,& encoded_param);
|
||||
parameters["debug_identifier"] = encoded_param;
|
||||
|
||||
if (!missing_info.version.empty()) {
|
||||
// The version is optional.
|
||||
WebSafeBase64Escape(missing_info.version,& encoded_param);
|
||||
parameters["version"] = encoded_param;
|
||||
}
|
||||
|
||||
WebSafeBase64Escape("WinSymConv",& encoded_param);
|
||||
parameters["product"] = encoded_param;
|
||||
|
||||
if (!StringMapToWStringMap(parameters, wparameters)) {
|
||||
// StringMapToWStringMap will have printed an error.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// UploadSymbolFile sends |converted_file| as identified by |debug_file| and
|
||||
// |debug_identifier|, to the symbol server rooted at |upload_symbol_url|.
|
||||
// Returns true on success and false on failure, printing an error message.
|
||||
static bool UploadSymbolFile(const wstring& upload_symbol_url,
|
||||
const wstring& api_key,
|
||||
const string& debug_file,
|
||||
const string& debug_identifier,
|
||||
const string& symbol_file,
|
||||
const wstring& symbol_type) {
|
||||
wstring debug_file_w;
|
||||
if (!WindowsStringUtils::safe_mbstowcs(debug_file, &debug_file_w)) {
|
||||
FprintfFlush(stderr, "UploadSymbolFile: safe_mbstowcs failed for %s\n",
|
||||
symbol_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
wstring debug_id_w;
|
||||
if (!WindowsStringUtils::safe_mbstowcs(debug_identifier, &debug_id_w)) {
|
||||
FprintfFlush(stderr, "UploadSymbolFile: safe_mbstowcs failed for %s\n",
|
||||
symbol_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
wstring symbol_file_w;
|
||||
if (!WindowsStringUtils::safe_mbstowcs(symbol_file, &symbol_file_w)) {
|
||||
FprintfFlush(stderr, "UploadSymbolFile: safe_mbstowcs failed for %s\n",
|
||||
symbol_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
int timeout_ms = 60 * 1000;
|
||||
FprintfFlush(stderr, "Uploading %s\n", symbol_file.c_str());
|
||||
if (!google_breakpad::SymUploadV2ProtocolSend(
|
||||
upload_symbol_url.c_str(), api_key.c_str(), &timeout_ms, debug_file_w,
|
||||
debug_id_w, symbol_file_w, symbol_type, kConverterProductName,
|
||||
/*force=*/true)) {
|
||||
FprintfFlush(stderr,
|
||||
"UploadSymbolFile: HTTPUpload::SendRequest failed "
|
||||
"for %s %s\n",
|
||||
debug_file.c_str(), debug_identifier.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// SendFetchFailedPing informs the symbol server based at
|
||||
// |fetch_symbol_failure_url| that the symbol file identified by
|
||||
// |missing_info| could authoritatively not be located. Returns
|
||||
// true on success and false on failure.
|
||||
static bool SendFetchFailedPing(const wstring& fetch_symbol_failure_url,
|
||||
const MissingSymbolInfo& missing_info) {
|
||||
map<wstring, wstring> parameters;
|
||||
if (!MissingSymbolInfoToParameters(missing_info,& parameters)) {
|
||||
// MissingSymbolInfoToParameters or a callee will have printed an error.
|
||||
return false;
|
||||
}
|
||||
|
||||
string content;
|
||||
if (!HTTPDownload::Download(fetch_symbol_failure_url,
|
||||
& parameters,
|
||||
& content,
|
||||
NULL)) {
|
||||
FprintfFlush(stderr, "SendFetchFailedPing: HTTPDownload::Download failed "
|
||||
"for %s %s %s\n",
|
||||
missing_info.debug_file.c_str(),
|
||||
missing_info.debug_identifier.c_str(),
|
||||
missing_info.version.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns true if it's safe to make an external request for the symbol
|
||||
// file described in missing_info. It's considered safe to make an
|
||||
// external request unless the symbol file's debug_file string matches
|
||||
// the given blacklist regular expression.
|
||||
// The debug_file name is used from the MissingSymbolInfo struct,
|
||||
// matched against the blacklist_regex.
|
||||
static bool SafeToMakeExternalRequest(const MissingSymbolInfo& missing_info,
|
||||
std::regex blacklist_regex) {
|
||||
string file_name = missing_info.debug_file;
|
||||
// Use regex_search because we want to match substrings.
|
||||
if (std::regex_search(file_name, blacklist_regex)) {
|
||||
FprintfFlush(stderr, "Not safe to make external request for file %s\n",
|
||||
file_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Converter options derived from command line parameters.
|
||||
struct ConverterOptions {
|
||||
ConverterOptions()
|
||||
: report_fetch_failures(true), trace_symsrv(false), keep_files(false) {}
|
||||
|
||||
~ConverterOptions() {
|
||||
}
|
||||
|
||||
// Names of MS Symbol Supplier Servers that are internal to Google, and may
|
||||
// have symbols for any request.
|
||||
vector<string> full_internal_msss_servers;
|
||||
|
||||
// Names of MS Symbol Supplier Servers that are internal to Google, and
|
||||
// shouldn't be checked for symbols for any .exe files.
|
||||
vector<string> full_external_msss_servers;
|
||||
|
||||
// Names of MS Symbol Supplier Servers that are external to Google, and may
|
||||
// have symbols for any request.
|
||||
vector<string> no_exe_internal_msss_servers;
|
||||
|
||||
// Names of MS Symbol Supplier Servers that are external to Google, and
|
||||
// shouldn't be checked for symbols for any .exe files.
|
||||
vector<string> no_exe_external_msss_servers;
|
||||
|
||||
// Temporary local storage for symbols.
|
||||
string local_cache_path;
|
||||
|
||||
// URL for uploading symbols.
|
||||
wstring upload_symbols_url;
|
||||
|
||||
// API key to use when uploading symbols.
|
||||
wstring api_key;
|
||||
|
||||
// URL to fetch list of missing symbols.
|
||||
wstring missing_symbols_url;
|
||||
|
||||
// URL to report symbol fetch failure.
|
||||
wstring fetch_symbol_failure_url;
|
||||
|
||||
// Are symbol fetch failures reported.
|
||||
bool report_fetch_failures;
|
||||
|
||||
// File containing the list of missing symbols. Fetch failures are not
|
||||
// reported if such file is provided.
|
||||
string missing_symbols_file;
|
||||
|
||||
// Regex used to blacklist files to prevent external symbol requests.
|
||||
// Owned and cleaned up by this struct.
|
||||
std::regex blacklist_regex;
|
||||
|
||||
// If set then SymSrv callbacks are logged to stderr.
|
||||
bool trace_symsrv;
|
||||
|
||||
// If set then Breakpad/PE/PDB files won't be deleted after processing.
|
||||
bool keep_files;
|
||||
|
||||
private:
|
||||
// DISABLE_COPY_AND_ASSIGN
|
||||
ConverterOptions(const ConverterOptions&);
|
||||
ConverterOptions& operator=(const ConverterOptions&);
|
||||
};
|
||||
|
||||
// ConverMissingSymbolFile takes a single MissingSymbolInfo structure and
|
||||
// attempts to locate it from the symbol servers provided in the
|
||||
// |options.*_msss_servers| arguments. "Full" servers are those that will be
|
||||
// queried for all symbol files; "No-EXE" servers will only be queried for
|
||||
// modules whose missing symbol data indicates are not main program executables.
|
||||
// Results will be sent to the |options.upload_symbols_url| on success or
|
||||
// |options.fetch_symbol_failure_url| on failure, and the local cache will be
|
||||
// stored at |options.local_cache_path|. Because nothing can be done even in
|
||||
// the event of a failure, this function returns no value, although it
|
||||
// may result in error messages being printed.
|
||||
static void ConvertMissingSymbolFile(const MissingSymbolInfo& missing_info,
|
||||
const ConverterOptions& options) {
|
||||
string time_string = CurrentDateAndTime();
|
||||
FprintfFlush(stdout, "converter: %s: attempting %s %s %s\n",
|
||||
time_string.c_str(),
|
||||
missing_info.debug_file.c_str(),
|
||||
missing_info.debug_identifier.c_str(),
|
||||
missing_info.version.c_str());
|
||||
|
||||
// The first lookup is always to internal symbol servers.
|
||||
// Always ask the symbol servers identified as "full."
|
||||
vector<string> msss_servers = options.full_internal_msss_servers;
|
||||
|
||||
// If the file is not an .exe file, also ask an additional set of symbol
|
||||
// servers, such as Microsoft's public symbol server.
|
||||
bool is_exe = false;
|
||||
|
||||
if (missing_info.code_file.length() >= 4) {
|
||||
string code_extension =
|
||||
missing_info.code_file.substr(missing_info.code_file.size() - 4);
|
||||
|
||||
// Firefox is a special case: .dll-only servers should be consulted for
|
||||
// its symbols. This enables us to get its symbols from Mozilla's
|
||||
// symbol server when crashes occur in Google extension code hosted by a
|
||||
// Firefox process.
|
||||
if (_stricmp(code_extension.c_str(), ".exe") == 0 &&
|
||||
_stricmp(missing_info.code_file.c_str(), "firefox.exe") != 0) {
|
||||
is_exe = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_exe) {
|
||||
msss_servers.insert(msss_servers.end(),
|
||||
options.no_exe_internal_msss_servers.begin(),
|
||||
options.no_exe_internal_msss_servers.end());
|
||||
}
|
||||
|
||||
// If there are any suitable internal symbol servers, make a request.
|
||||
MSSymbolServerConverter::LocateResult located =
|
||||
MSSymbolServerConverter::LOCATE_FAILURE;
|
||||
string converted_file;
|
||||
string symbol_file;
|
||||
string pe_file;
|
||||
if (msss_servers.size() > 0) {
|
||||
// Attempt to fetch the symbol file and convert it.
|
||||
FprintfFlush(stderr, "Making internal request for %s (%s)\n",
|
||||
missing_info.debug_file.c_str(),
|
||||
missing_info.debug_identifier.c_str());
|
||||
MSSymbolServerConverter converter(options.local_cache_path, msss_servers,
|
||||
options.trace_symsrv);
|
||||
located = converter.LocateAndConvertSymbolFile(
|
||||
missing_info,
|
||||
/*keep_symbol_file=*/true,
|
||||
/*keep_pe_file=*/true, &converted_file, &symbol_file, &pe_file);
|
||||
switch (located) {
|
||||
case MSSymbolServerConverter::LOCATE_SUCCESS:
|
||||
FprintfFlush(stderr, "LocateResult = LOCATE_SUCCESS\n");
|
||||
// Upload it. Don't bother checking the return value. If this
|
||||
// succeeds, it should disappear from the missing symbol list.
|
||||
// If it fails, something will print an error message indicating
|
||||
// the cause of the failure, and the item will remain on the
|
||||
// missing symbol list.
|
||||
UploadSymbolFile(options.upload_symbols_url, options.api_key,
|
||||
missing_info.debug_file, missing_info.debug_identifier,
|
||||
converted_file, kSymbolUploadTypeBreakpad);
|
||||
if (!options.keep_files)
|
||||
remove(converted_file.c_str());
|
||||
|
||||
// Upload PDB/PE if we have them
|
||||
if (!symbol_file.empty()) {
|
||||
UploadSymbolFile(options.upload_symbols_url, options.api_key,
|
||||
missing_info.debug_file,
|
||||
missing_info.debug_identifier, symbol_file,
|
||||
kSymbolUploadTypePDB);
|
||||
if (!options.keep_files)
|
||||
remove(symbol_file.c_str());
|
||||
}
|
||||
if (!pe_file.empty()) {
|
||||
UploadSymbolFile(options.upload_symbols_url, options.api_key,
|
||||
missing_info.code_file,
|
||||
missing_info.debug_identifier, pe_file,
|
||||
kSymbolUploadTypePE);
|
||||
if (!options.keep_files)
|
||||
remove(pe_file.c_str());
|
||||
}
|
||||
|
||||
// Note: this does leave some directories behind that could be
|
||||
// cleaned up. The directories inside options.local_cache_path for
|
||||
// debug_file/debug_identifier can be removed at this point.
|
||||
break;
|
||||
|
||||
case MSSymbolServerConverter::LOCATE_NOT_FOUND:
|
||||
FprintfFlush(stderr, "LocateResult = LOCATE_NOT_FOUND\n");
|
||||
// The symbol file definitively did not exist. Fall through,
|
||||
// so we can attempt an external query if it's safe to do so.
|
||||
break;
|
||||
|
||||
case MSSymbolServerConverter::LOCATE_RETRY:
|
||||
FprintfFlush(stderr, "LocateResult = LOCATE_RETRY\n");
|
||||
// Fall through in case we should make an external request.
|
||||
// If not, or if an external request fails in the same way,
|
||||
// we'll leave the entry in the symbol file list and
|
||||
// try again on a future pass. Print a message so that there's
|
||||
// a record.
|
||||
break;
|
||||
|
||||
case MSSymbolServerConverter::LOCATE_HTTP_HTTPS_REDIR:
|
||||
FprintfFlush(
|
||||
stderr,
|
||||
"LocateResult = LOCATE_HTTP_HTTPS_REDIR\n"
|
||||
"One of the specified URLs is using HTTP, which causes a redirect "
|
||||
"from the server to HTTPS, which causes the SymSrv lookup to "
|
||||
"fail.\n"
|
||||
"This URL must be replaced with the correct HTTPS URL.\n");
|
||||
break;
|
||||
|
||||
case MSSymbolServerConverter::LOCATE_FAILURE:
|
||||
FprintfFlush(stderr, "LocateResult = LOCATE_FAILURE\n");
|
||||
// LocateAndConvertSymbolFile printed an error message.
|
||||
break;
|
||||
|
||||
default:
|
||||
FprintfFlush(
|
||||
stderr,
|
||||
"FATAL: Unexpected return value '%d' from "
|
||||
"LocateAndConvertSymbolFile()\n",
|
||||
located);
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// No suitable internal symbol servers. This is fine because the converter
|
||||
// is mainly used for downloading and converting of external symbols.
|
||||
}
|
||||
|
||||
// Make a request to an external server if the internal request didn't
|
||||
// succeed, and it's safe to do so.
|
||||
if (located != MSSymbolServerConverter::LOCATE_SUCCESS &&
|
||||
SafeToMakeExternalRequest(missing_info, options.blacklist_regex)) {
|
||||
msss_servers = options.full_external_msss_servers;
|
||||
if (!is_exe) {
|
||||
msss_servers.insert(msss_servers.end(),
|
||||
options.no_exe_external_msss_servers.begin(),
|
||||
options.no_exe_external_msss_servers.end());
|
||||
}
|
||||
if (msss_servers.size() > 0) {
|
||||
FprintfFlush(stderr, "Making external request for %s (%s)\n",
|
||||
missing_info.debug_file.c_str(),
|
||||
missing_info.debug_identifier.c_str());
|
||||
MSSymbolServerConverter external_converter(
|
||||
options.local_cache_path, msss_servers, options.trace_symsrv);
|
||||
located = external_converter.LocateAndConvertSymbolFile(
|
||||
missing_info,
|
||||
/*keep_symbol_file=*/true,
|
||||
/*keep_pe_file=*/true, &converted_file, &symbol_file, &pe_file);
|
||||
} else {
|
||||
FprintfFlush(stderr, "ERROR: No suitable external symbol servers.\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Final handling for this symbol file is based on the result from the
|
||||
// external request (if performed above), or on the result from the
|
||||
// previous internal lookup.
|
||||
switch (located) {
|
||||
case MSSymbolServerConverter::LOCATE_SUCCESS:
|
||||
FprintfFlush(stderr, "LocateResult = LOCATE_SUCCESS\n");
|
||||
// Upload it. Don't bother checking the return value. If this
|
||||
// succeeds, it should disappear from the missing symbol list.
|
||||
// If it fails, something will print an error message indicating
|
||||
// the cause of the failure, and the item will remain on the
|
||||
// missing symbol list.
|
||||
UploadSymbolFile(options.upload_symbols_url, options.api_key,
|
||||
missing_info.debug_file, missing_info.debug_identifier,
|
||||
converted_file, kSymbolUploadTypeBreakpad);
|
||||
if (!options.keep_files)
|
||||
remove(converted_file.c_str());
|
||||
|
||||
// Upload PDB/PE if we have them
|
||||
if (!symbol_file.empty()) {
|
||||
UploadSymbolFile(options.upload_symbols_url, options.api_key,
|
||||
missing_info.debug_file, missing_info.debug_identifier,
|
||||
symbol_file, kSymbolUploadTypePDB);
|
||||
if (!options.keep_files)
|
||||
remove(symbol_file.c_str());
|
||||
}
|
||||
if (!pe_file.empty()) {
|
||||
UploadSymbolFile(options.upload_symbols_url, options.api_key,
|
||||
missing_info.code_file, missing_info.debug_identifier,
|
||||
pe_file, kSymbolUploadTypePE);
|
||||
if (!options.keep_files)
|
||||
remove(pe_file.c_str());
|
||||
}
|
||||
|
||||
// Note: this does leave some directories behind that could be
|
||||
// cleaned up. The directories inside options.local_cache_path for
|
||||
// debug_file/debug_identifier can be removed at this point.
|
||||
break;
|
||||
|
||||
case MSSymbolServerConverter::LOCATE_NOT_FOUND:
|
||||
// The symbol file definitively didn't exist. Inform the server.
|
||||
// If this fails, something will print an error message indicating
|
||||
// the cause of the failure, but there's really nothing more to
|
||||
// do. If this succeeds, the entry should be removed from the
|
||||
// missing symbols list.
|
||||
if (!options.report_fetch_failures) {
|
||||
FprintfFlush(stderr, "SendFetchFailedPing skipped\n");
|
||||
} else if (SendFetchFailedPing(options.fetch_symbol_failure_url,
|
||||
missing_info)) {
|
||||
FprintfFlush(stderr, "SendFetchFailedPing succeeded\n");
|
||||
} else {
|
||||
FprintfFlush(stderr, "SendFetchFailedPing failed\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case MSSymbolServerConverter::LOCATE_RETRY:
|
||||
FprintfFlush(stderr, "LocateResult = LOCATE_RETRY\n");
|
||||
// Nothing to do but leave the entry in the symbol file list and
|
||||
// try again on a future pass. Print a message so that there's
|
||||
// a record.
|
||||
FprintfFlush(stderr, "ConvertMissingSymbolFile: deferring retry "
|
||||
"for %s %s %s\n",
|
||||
missing_info.debug_file.c_str(),
|
||||
missing_info.debug_identifier.c_str(),
|
||||
missing_info.version.c_str());
|
||||
break;
|
||||
|
||||
case MSSymbolServerConverter::LOCATE_HTTP_HTTPS_REDIR:
|
||||
FprintfFlush(
|
||||
stderr,
|
||||
"LocateResult = LOCATE_HTTP_HTTPS_REDIR\n"
|
||||
"One of the specified URLs is using HTTP, which causes a redirect "
|
||||
"from the server to HTTPS, which causes the SymSrv lookup to fail.\n"
|
||||
"This URL must be replaced with the correct HTTPS URL.\n");
|
||||
break;
|
||||
|
||||
case MSSymbolServerConverter::LOCATE_FAILURE:
|
||||
FprintfFlush(stderr, "LocateResult = LOCATE_FAILURE\n");
|
||||
// LocateAndConvertSymbolFile printed an error message.
|
||||
|
||||
// This is due to a bad debug file name, so fetch failed.
|
||||
if (!options.report_fetch_failures) {
|
||||
FprintfFlush(stderr, "SendFetchFailedPing skipped\n");
|
||||
} else if (SendFetchFailedPing(options.fetch_symbol_failure_url,
|
||||
missing_info)) {
|
||||
FprintfFlush(stderr, "SendFetchFailedPing succeeded\n");
|
||||
} else {
|
||||
FprintfFlush(stderr, "SendFetchFailedPing failed\n");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
FprintfFlush(
|
||||
stderr,
|
||||
"FATAL: Unexpected return value '%d' from "
|
||||
"LocateAndConvertSymbolFile()\n",
|
||||
located);
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reads the contents of file |file_name| and populates |contents|.
|
||||
// Returns true on success.
|
||||
static bool ReadFile(string file_name, string* contents) {
|
||||
char buffer[1024 * 8];
|
||||
FILE* fp = fopen(file_name.c_str(), "rt");
|
||||
if (!fp) {
|
||||
return false;
|
||||
}
|
||||
contents->clear();
|
||||
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
|
||||
contents->append(buffer);
|
||||
}
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ConvertMissingSymbolsList obtains a missing symbol list from
|
||||
// |options.missing_symbols_url| or |options.missing_symbols_file| and calls
|
||||
// ConvertMissingSymbolFile for each missing symbol file in the list.
|
||||
static bool ConvertMissingSymbolsList(const ConverterOptions& options) {
|
||||
// Set param to indicate requesting for encoded response.
|
||||
map<wstring, wstring> parameters;
|
||||
parameters[L"product"] = kConverterProductName;
|
||||
parameters[L"encoded"] = L"true";
|
||||
// Get the missing symbol list.
|
||||
string missing_symbol_list;
|
||||
if (!options.missing_symbols_file.empty()) {
|
||||
if (!ReadFile(options.missing_symbols_file,& missing_symbol_list)) {
|
||||
return false;
|
||||
}
|
||||
} else if (!HTTPDownload::Download(options.missing_symbols_url,& parameters,
|
||||
& missing_symbol_list, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Tokenize the content into a vector.
|
||||
vector<string> missing_symbol_lines;
|
||||
Tokenizer::Tokenize("\n", missing_symbol_list,& missing_symbol_lines);
|
||||
|
||||
FprintfFlush(stderr, "Found %d missing symbol files in list.\n",
|
||||
missing_symbol_lines.size() - 1); // last line is empty.
|
||||
int convert_attempts = 0;
|
||||
for (vector<string>::const_iterator iterator = missing_symbol_lines.begin();
|
||||
iterator != missing_symbol_lines.end();
|
||||
++iterator) {
|
||||
// Decode symbol line.
|
||||
const string& encoded_line = *iterator;
|
||||
// Skip lines that are blank.
|
||||
if (encoded_line.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
string line;
|
||||
if (!WebSafeBase64Unescape(encoded_line,& line)) {
|
||||
// If decoding fails, assume the line is not encoded.
|
||||
// This is helpful when the program connects to a debug server without
|
||||
// encoding.
|
||||
line = encoded_line;
|
||||
}
|
||||
|
||||
FprintfFlush(stderr, "\nLine: %s\n", line.c_str());
|
||||
|
||||
// Turn each element into a MissingSymbolInfo structure.
|
||||
MissingSymbolInfo missing_info;
|
||||
if (!ParseMissingString(line,& missing_info)) {
|
||||
FprintfFlush(stderr, "ConvertMissingSymbols: ParseMissingString failed "
|
||||
"for %s from %ws\n",
|
||||
line.c_str(), options.missing_symbols_url.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
++convert_attempts;
|
||||
ConvertMissingSymbolFile(missing_info, options);
|
||||
}
|
||||
|
||||
// Say something reassuring, since ConvertMissingSymbolFile was never called
|
||||
// and therefore never reported any progress.
|
||||
if (convert_attempts == 0) {
|
||||
string current_time = CurrentDateAndTime();
|
||||
FprintfFlush(stdout, "converter: %s: nothing to convert\n",
|
||||
current_time.c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// usage prints the usage message. It returns 1 as a convenience, to be used
|
||||
// as a return value from main.
|
||||
static int usage(const char* program_name) {
|
||||
FprintfFlush(
|
||||
stderr,
|
||||
"usage: %s [options]\n"
|
||||
" -f <full_msss_server> MS servers to ask for all symbols\n"
|
||||
" -n <no_exe_msss_server> same, but prevent asking for EXEs\n"
|
||||
" -l <local_cache_path> Temporary local storage for symbols\n"
|
||||
" -s <upload_url> URL for uploading symbols\n"
|
||||
" -k <api_key> API key to use when uploading symbols\n"
|
||||
" -m <missing_symbols_url> URL to fetch list of missing symbols\n"
|
||||
" -mf <missing_symbols_file> File containing the list of missing\n"
|
||||
" symbols. Fetch failures are not\n"
|
||||
" reported if such file is provided.\n"
|
||||
" -t <fetch_failure_url> URL to report symbol fetch failure\n"
|
||||
" -b <regex> Regex used to blacklist files to\n"
|
||||
" prevent external symbol requests\n"
|
||||
" -tss If set then SymSrv callbacks will be\n"
|
||||
" traced to stderr.\n"
|
||||
" -keep-files If set then don't delete Breakpad/PE/\n"
|
||||
" PDB files after conversion.\n"
|
||||
" Note that any server specified by -f or -n that starts with \\filer\n"
|
||||
" will be treated as internal, and all others as external.\n",
|
||||
program_name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// "Internal" servers consist only of those whose names start with
|
||||
// the literal string "\\filer\".
|
||||
static bool IsInternalServer(const string& server_name) {
|
||||
if (server_name.find("\\\\filer\\") == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Adds a server with the given name to the list of internal or external
|
||||
// servers, as appropriate.
|
||||
static void AddServer(const string& server_name,
|
||||
vector<string>* internal_servers,
|
||||
vector<string>* external_servers) {
|
||||
if (IsInternalServer(server_name)) {
|
||||
internal_servers->push_back(server_name);
|
||||
} else {
|
||||
external_servers->push_back(server_name);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
string time_string = CurrentDateAndTime();
|
||||
FprintfFlush(stdout, "converter: %s: starting\n", time_string.c_str());
|
||||
|
||||
ConverterOptions options;
|
||||
options.report_fetch_failures = true;
|
||||
|
||||
string blacklist_regex_str;
|
||||
bool have_any_msss_servers = false;
|
||||
for (int argi = 1; argi < argc; argi++) {
|
||||
string option = argv[argi];
|
||||
if (option == "-tss") {
|
||||
printf("Tracing SymSrv callbacks to stderr.\n");
|
||||
options.trace_symsrv = true;
|
||||
continue;
|
||||
} else if (option == "-keep-files") {
|
||||
printf("Keeping Breakpad/PE/PDB files after conversion.\n");
|
||||
options.keep_files = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
string value = argv[++argi];
|
||||
if (option == "-f") {
|
||||
AddServer(value,& options.full_internal_msss_servers,
|
||||
& options.full_external_msss_servers);
|
||||
have_any_msss_servers = true;
|
||||
} else if (option == "-n") {
|
||||
AddServer(value,& options.no_exe_internal_msss_servers,
|
||||
& options.no_exe_external_msss_servers);
|
||||
have_any_msss_servers = true;
|
||||
} else if (option == "-l") {
|
||||
if (!options.local_cache_path.empty()) {
|
||||
return usage(argv[0]);
|
||||
}
|
||||
options.local_cache_path = value;
|
||||
} else if (option == "-s") {
|
||||
if (!WindowsStringUtils::safe_mbstowcs(value,
|
||||
& options.upload_symbols_url)) {
|
||||
FprintfFlush(stderr, "main: safe_mbstowcs failed for %s\n",
|
||||
value.c_str());
|
||||
return 1;
|
||||
}
|
||||
} else if (option == "-k") {
|
||||
if (!WindowsStringUtils::safe_mbstowcs(value, &options.api_key)) {
|
||||
FprintfFlush(stderr, "main: safe_mbstowcs failed for %s\n",
|
||||
value.c_str());
|
||||
return 1;
|
||||
}
|
||||
} else if (option == "-m") {
|
||||
if (!WindowsStringUtils::safe_mbstowcs(value,
|
||||
& options.missing_symbols_url)) {
|
||||
FprintfFlush(stderr, "main: safe_mbstowcs failed for %s\n",
|
||||
value.c_str());
|
||||
return 1;
|
||||
}
|
||||
} else if (option == "-mf") {
|
||||
options.missing_symbols_file = value;
|
||||
printf("Getting the list of missing symbols from a file. Fetch failures"
|
||||
" will not be reported.\n");
|
||||
options.report_fetch_failures = false;
|
||||
} else if (option == "-t") {
|
||||
if (!WindowsStringUtils::safe_mbstowcs(
|
||||
value,
|
||||
& options.fetch_symbol_failure_url)) {
|
||||
FprintfFlush(stderr, "main: safe_mbstowcs failed for %s\n",
|
||||
value.c_str());
|
||||
return 1;
|
||||
}
|
||||
} else if (option == "-b") {
|
||||
blacklist_regex_str = value;
|
||||
} else {
|
||||
return usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (blacklist_regex_str.empty()) {
|
||||
FprintfFlush(stderr, "No blacklist specified.\n");
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
// Compile the blacklist regular expression for later use.
|
||||
options.blacklist_regex = std::regex(blacklist_regex_str.c_str(),
|
||||
std::regex_constants::icase);
|
||||
|
||||
// Set the defaults. If the user specified any MSSS servers, don't use
|
||||
// any default.
|
||||
if (!have_any_msss_servers) {
|
||||
AddServer(kNoExeMSSSServer,& options.no_exe_internal_msss_servers,
|
||||
& options.no_exe_external_msss_servers);
|
||||
}
|
||||
|
||||
if (options.local_cache_path.empty()) {
|
||||
options.local_cache_path = kLocalCachePath;
|
||||
}
|
||||
|
||||
if (options.upload_symbols_url.empty()) {
|
||||
FprintfFlush(stderr, "No upload symbols URL specified.\n");
|
||||
return usage(argv[0]);
|
||||
}
|
||||
if (options.api_key.empty()) {
|
||||
FprintfFlush(stderr, "No API key specified.\n");
|
||||
return usage(argv[0]);
|
||||
}
|
||||
if (options.missing_symbols_url.empty() &&
|
||||
options.missing_symbols_file.empty()) {
|
||||
FprintfFlush(stderr, "No missing symbols URL or file specified.\n");
|
||||
return usage(argv[0]);
|
||||
}
|
||||
if (options.fetch_symbol_failure_url.empty()) {
|
||||
FprintfFlush(stderr, "No fetch symbol failure URL specified.\n");
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
FprintfFlush(stdout,
|
||||
"# of Symbol Servers (int/ext): %d/%d full, %d/%d no_exe\n",
|
||||
options.full_internal_msss_servers.size(),
|
||||
options.full_external_msss_servers.size(),
|
||||
options.no_exe_internal_msss_servers.size(),
|
||||
options.no_exe_external_msss_servers.size());
|
||||
|
||||
if (!ConvertMissingSymbolsList(options)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
time_string = CurrentDateAndTime();
|
||||
FprintfFlush(stdout, "converter: %s: finished\n", time_string.c_str());
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue