Source code

Revision control

Copy as Markdown

Other Tools

// Copyright 2019 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.↩
#pragma comment(lib, "winhttp.lib")↩
#pragma comment(lib, "wininet.lib")↩
#pragma comment(lib, "diaguids.lib")↩
#pragma comment(lib, "imagehlp.lib")↩
#include <cassert>↩
#include <cstdio>↩
#include <ctime>↩
#include <map>↩
#include <regex>↩
#include <string>↩
#include <vector>↩
#include "tools/windows/converter_exe/escaping.h"
#include "tools/windows/converter_exe/http_download.h"
#include "tools/windows/converter_exe/tokenizer.h"
#include "common/windows/http_upload.h"
#include "common/windows/string_utils-inl.h"
#include "tools/windows/converter/ms_symbol_server_converter.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/";↩
// 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(&current_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(&current_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 |missing_info|↩
// 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 MissingSymbolInfo &missing_info,↩
const string &converted_file) {↩
map<wstring, wstring> parameters;↩
if (!MissingSymbolInfoToParameters(missing_info, &parameters)) {↩
// MissingSymbolInfoToParameters or a callee will have printed an error.↩
return false;↩
}↩
wstring converted_file_w;↩
if (!WindowsStringUtils::safe_mbstowcs(converted_file, &converted_file_w)) {↩
FprintfFlush(stderr, "UploadSymbolFile: safe_mbstowcs failed for %s\n",↩
converted_file.c_str());↩
return false;↩
}↩
map<wstring, wstring> files;↩
files[L"symbol_file"] = converted_file_w;↩
FprintfFlush(stderr, "Uploading %s\n", converted_file.c_str());↩
if (!HTTPUpload::SendMultipartPostRequest(↩
upload_symbol_url, parameters,↩
files, NULL, NULL, NULL)) {↩
FprintfFlush(stderr, "UploadSymbolFile: HTTPUpload::SendRequest 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;↩
}↩
// 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) {↩
}↩
~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;↩
// 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;↩
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;↩
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);↩
located = converter.LocateAndConvertSymbolFile(missing_info,↩
false, // keep_symbol_file↩
false, // keep_pe_file↩
&converted_file,↩
NULL, // symbol_file↩
NULL); // 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, missing_info,↩
converted_file);↩
remove(converted_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_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);↩
located = external_converter.LocateAndConvertSymbolFile(↩
missing_info,↩
false, // keep_symbol_file↩
false, // keep_pe_file↩
&converted_file,↩
NULL, // symbol_file↩
NULL); // 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, missing_info,↩
converted_file);↩
remove(converted_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_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"] = L"WinSymConv";↩
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"
" -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"
" 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;↩
// All arguments are paired.↩
if (argc % 2 != 1) {↩
return usage(argv[0]);↩
}↩
string blacklist_regex_str;↩
bool have_any_msss_servers = false;↩
for (int argi = 1; argi < argc; argi += 2) {↩
string option = argv[argi];↩
string value = argv[argi + 1];↩
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 == "-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.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;↩
}↩