• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 Google Inc. All rights reserved.
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 //     * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //     * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 //     * Neither the name of Google Inc. nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #pragma comment(lib, "winhttp.lib")
30 #pragma comment(lib, "wininet.lib")
31 #pragma comment(lib, "diaguids.lib")
32 #pragma comment(lib, "imagehlp.lib")
33 
34 #include <cassert>
35 #include <cstdio>
36 #include <ctime>
37 #include <map>
38 #include <regex>
39 #include <string>
40 #include <vector>
41 
42 #include "tools/windows/converter_exe/escaping.h"
43 #include "tools/windows/converter_exe/http_download.h"
44 #include "tools/windows/converter_exe/tokenizer.h"
45 #include "common/windows/http_upload.h"
46 #include "common/windows/string_utils-inl.h"
47 #include "tools/windows/converter/ms_symbol_server_converter.h"
48 
49 using strings::WebSafeBase64Unescape;
50 using strings::WebSafeBase64Escape;
51 
52 namespace {
53 
54 using std::map;
55 using std::string;
56 using std::vector;
57 using std::wstring;
58 using crash::HTTPDownload;
59 using crash::Tokenizer;
60 using google_breakpad::HTTPUpload;
61 using google_breakpad::MissingSymbolInfo;
62 using google_breakpad::MSSymbolServerConverter;
63 using google_breakpad::WindowsStringUtils;
64 
65 const char *kMissingStringDelimiters = "|";
66 const char *kLocalCachePath = "c:\\symbols";
67 const char *kNoExeMSSSServer = "http://msdl.microsoft.com/download/symbols/";
68 
69 // Windows stdio doesn't do line buffering.  Use this function to flush after
70 // writing to stdout and stderr so that a log will be available if the
71 // converter crashes.
FprintfFlush(FILE * file,const char * format,...)72 static int FprintfFlush(FILE *file, const char *format, ...) {
73   va_list arguments;
74   va_start(arguments, format);
75   int retval = vfprintf(file, format, arguments);
76   va_end(arguments);
77   fflush(file);
78   return retval;
79 }
80 
CurrentDateAndTime()81 static string CurrentDateAndTime() {
82   const string kUnknownDateAndTime = R"(????-??-?? ??:??:??)";
83 
84   time_t current_time;
85   time(&current_time);
86 
87   // localtime_s is safer but is only available in MSVC8.  Use localtime
88   // in earlier environments.
89   struct tm *time_pointer;
90 #if _MSC_VER >= 1400  // MSVC 2005/8
91   struct tm time_struct;
92   time_pointer = &time_struct;
93   if (localtime_s(time_pointer, &current_time) != 0) {
94     return kUnknownDateAndTime;
95   }
96 #else  // _MSC_VER >= 1400
97   time_pointer = localtime(&current_time);
98   if (!time_pointer) {
99     return kUnknownDateAndTime;
100   }
101 #endif  // _MSC_VER >= 1400
102 
103   char buffer[256];
104   if (!strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", time_pointer)) {
105     return kUnknownDateAndTime;
106   }
107 
108   return string(buffer);
109 }
110 
111 // ParseMissingString turns |missing_string| into a MissingSymbolInfo
112 // structure.  It returns true on success, and false if no such conversion
113 // is possible.
ParseMissingString(const string & missing_string,MissingSymbolInfo * missing_info)114 static bool ParseMissingString(const string &missing_string,
115                                MissingSymbolInfo *missing_info) {
116   assert(missing_info);
117 
118   vector<string> tokens;
119   Tokenizer::Tokenize(kMissingStringDelimiters, missing_string, &tokens);
120   if (tokens.size() != 5) {
121     return false;
122   }
123 
124   missing_info->debug_file = tokens[0];
125   missing_info->debug_identifier = tokens[1];
126   missing_info->version = tokens[2];
127   missing_info->code_file = tokens[3];
128   missing_info->code_identifier = tokens[4];
129 
130   return true;
131 }
132 
133 // StringMapToWStringMap takes each element in a map that associates
134 // (narrow) strings to strings and converts the keys and values to wstrings.
135 // Returns true on success and false on failure, printing an error message.
StringMapToWStringMap(const map<string,string> & smap,map<wstring,wstring> * wsmap)136 static bool StringMapToWStringMap(const map<string, string> &smap,
137                                   map<wstring, wstring> *wsmap) {
138   assert(wsmap);
139   wsmap->clear();
140 
141   for (map<string, string>::const_iterator iterator = smap.begin();
142        iterator != smap.end();
143        ++iterator) {
144     wstring key;
145     if (!WindowsStringUtils::safe_mbstowcs(iterator->first, &key)) {
146       FprintfFlush(stderr,
147                    "StringMapToWStringMap: safe_mbstowcs failed for key %s\n",
148                    iterator->first.c_str());
149       return false;
150     }
151 
152     wstring value;
153     if (!WindowsStringUtils::safe_mbstowcs(iterator->second, &value)) {
154       FprintfFlush(stderr, "StringMapToWStringMap: safe_mbstowcs failed "
155                            "for value %s\n",
156                    iterator->second.c_str());
157       return false;
158     }
159 
160     wsmap->insert(make_pair(key, value));
161   }
162 
163   return true;
164 }
165 
166 // MissingSymbolInfoToParameters turns a MissingSymbolInfo structure into a
167 // map of parameters suitable for passing to HTTPDownload or HTTPUpload.
168 // Returns true on success and false on failure, printing an error message.
MissingSymbolInfoToParameters(const MissingSymbolInfo & missing_info,map<wstring,wstring> * wparameters)169 static bool MissingSymbolInfoToParameters(const MissingSymbolInfo &missing_info,
170                                           map<wstring, wstring> *wparameters) {
171   assert(wparameters);
172 
173   map<string, string> parameters;
174   string encoded_param;
175   // Indicate the params are encoded.
176   parameters["encoded"] = "true";  // The string value here does not matter.
177 
178   WebSafeBase64Escape(missing_info.code_file, &encoded_param);
179   parameters["code_file"] = encoded_param;
180 
181   WebSafeBase64Escape(missing_info.code_identifier, &encoded_param);
182   parameters["code_identifier"] = encoded_param;
183 
184   WebSafeBase64Escape(missing_info.debug_file, &encoded_param);
185   parameters["debug_file"] = encoded_param;
186 
187   WebSafeBase64Escape(missing_info.debug_identifier, &encoded_param);
188   parameters["debug_identifier"] = encoded_param;
189 
190   if (!missing_info.version.empty()) {
191     // The version is optional.
192     WebSafeBase64Escape(missing_info.version, &encoded_param);
193     parameters["version"] = encoded_param;
194   }
195 
196   WebSafeBase64Escape("WinSymConv", &encoded_param);
197   parameters["product"] = encoded_param;
198 
199   if (!StringMapToWStringMap(parameters, wparameters)) {
200     // StringMapToWStringMap will have printed an error.
201     return false;
202   }
203 
204   return true;
205 }
206 
207 // UploadSymbolFile sends |converted_file| as identified by |missing_info|
208 // to the symbol server rooted at |upload_symbol_url|.  Returns true on
209 // success and false on failure, printing an error message.
UploadSymbolFile(const wstring & upload_symbol_url,const MissingSymbolInfo & missing_info,const string & converted_file)210 static bool UploadSymbolFile(const wstring &upload_symbol_url,
211                              const MissingSymbolInfo &missing_info,
212                              const string &converted_file) {
213   map<wstring, wstring> parameters;
214   if (!MissingSymbolInfoToParameters(missing_info, &parameters)) {
215     // MissingSymbolInfoToParameters or a callee will have printed an error.
216     return false;
217   }
218 
219   wstring converted_file_w;
220 
221   if (!WindowsStringUtils::safe_mbstowcs(converted_file, &converted_file_w)) {
222     FprintfFlush(stderr, "UploadSymbolFile: safe_mbstowcs failed for %s\n",
223                  converted_file.c_str());
224     return false;
225   }
226   map<wstring, wstring> files;
227   files[L"symbol_file"] = converted_file_w;
228 
229   FprintfFlush(stderr, "Uploading %s\n", converted_file.c_str());
230   if (!HTTPUpload::SendMultipartPostRequest(
231       upload_symbol_url, parameters,
232       files, NULL, NULL, NULL)) {
233     FprintfFlush(stderr, "UploadSymbolFile: HTTPUpload::SendRequest failed "
234                          "for %s %s %s\n",
235                  missing_info.debug_file.c_str(),
236                  missing_info.debug_identifier.c_str(),
237                  missing_info.version.c_str());
238     return false;
239   }
240 
241   return true;
242 }
243 
244 // SendFetchFailedPing informs the symbol server based at
245 // |fetch_symbol_failure_url| that the symbol file identified by
246 // |missing_info| could authoritatively not be located.  Returns
247 // true on success and false on failure.
SendFetchFailedPing(const wstring & fetch_symbol_failure_url,const MissingSymbolInfo & missing_info)248 static bool SendFetchFailedPing(const wstring &fetch_symbol_failure_url,
249                                 const MissingSymbolInfo &missing_info) {
250   map<wstring, wstring> parameters;
251   if (!MissingSymbolInfoToParameters(missing_info, &parameters)) {
252     // MissingSymbolInfoToParameters or a callee will have printed an error.
253     return false;
254   }
255 
256   string content;
257   if (!HTTPDownload::Download(fetch_symbol_failure_url,
258                               &parameters,
259                               &content,
260                               NULL)) {
261     FprintfFlush(stderr, "SendFetchFailedPing: HTTPDownload::Download failed "
262                          "for %s %s %s\n",
263                  missing_info.debug_file.c_str(),
264                  missing_info.debug_identifier.c_str(),
265                  missing_info.version.c_str());
266     return false;
267   }
268 
269   return true;
270 }
271 
272 // Returns true if it's safe to make an external request for the symbol
273 // file described in missing_info. It's considered safe to make an
274 // external request unless the symbol file's debug_file string matches
275 // the given blacklist regular expression.
276 // The debug_file name is used from the MissingSymbolInfo struct,
277 // matched against the blacklist_regex.
SafeToMakeExternalRequest(const MissingSymbolInfo & missing_info,std::regex blacklist_regex)278 static bool SafeToMakeExternalRequest(const MissingSymbolInfo &missing_info,
279                                       std::regex blacklist_regex) {
280   string file_name = missing_info.debug_file;
281   // Use regex_search because we want to match substrings.
282   if (std::regex_search(file_name, blacklist_regex)) {
283     FprintfFlush(stderr, "Not safe to make external request for file %s\n",
284                  file_name.c_str());
285     return false;
286   }
287 
288   return true;
289 }
290 
291 // Converter options derived from command line parameters.
292 struct ConverterOptions {
ConverterOptions__anonb54e8ea90111::ConverterOptions293   ConverterOptions()
294       : report_fetch_failures(true) {
295   }
296 
~ConverterOptions__anonb54e8ea90111::ConverterOptions297   ~ConverterOptions() {
298   }
299 
300   // Names of MS Symbol Supplier Servers that are internal to Google, and may
301   // have symbols for any request.
302   vector<string> full_internal_msss_servers;
303 
304   // Names of MS Symbol Supplier Servers that are internal to Google, and
305   // shouldn't be checked for symbols for any .exe files.
306   vector<string> full_external_msss_servers;
307 
308   // Names of MS Symbol Supplier Servers that are external to Google, and may
309   // have symbols for any request.
310   vector<string> no_exe_internal_msss_servers;
311 
312   // Names of MS Symbol Supplier Servers that are external to Google, and
313   // shouldn't be checked for symbols for any .exe files.
314   vector<string> no_exe_external_msss_servers;
315 
316   // Temporary local storage for symbols.
317   string local_cache_path;
318 
319   // URL for uploading symbols.
320   wstring upload_symbols_url;
321 
322   // URL to fetch list of missing symbols.
323   wstring missing_symbols_url;
324 
325   // URL to report symbol fetch failure.
326   wstring fetch_symbol_failure_url;
327 
328   // Are symbol fetch failures reported.
329   bool report_fetch_failures;
330 
331   // File containing the list of missing symbols.  Fetch failures are not
332   // reported if such file is provided.
333   string missing_symbols_file;
334 
335   // Regex used to blacklist files to prevent external symbol requests.
336   // Owned and cleaned up by this struct.
337   std::regex blacklist_regex;
338 
339  private:
340   // DISABLE_COPY_AND_ASSIGN
341   ConverterOptions(const ConverterOptions&);
342   ConverterOptions& operator=(const ConverterOptions&);
343 };
344 
345 // ConverMissingSymbolFile takes a single MissingSymbolInfo structure and
346 // attempts to locate it from the symbol servers provided in the
347 // |options.*_msss_servers| arguments.  "Full" servers are those that will be
348 // queried for all symbol files; "No-EXE" servers will only be queried for
349 // modules whose missing symbol data indicates are not main program executables.
350 // Results will be sent to the |options.upload_symbols_url| on success or
351 // |options.fetch_symbol_failure_url| on failure, and the local cache will be
352 // stored at |options.local_cache_path|.  Because nothing can be done even in
353 // the event of a failure, this function returns no value, although it
354 // may result in error messages being printed.
ConvertMissingSymbolFile(const MissingSymbolInfo & missing_info,const ConverterOptions & options)355 static void ConvertMissingSymbolFile(const MissingSymbolInfo &missing_info,
356                                      const ConverterOptions &options) {
357   string time_string = CurrentDateAndTime();
358   FprintfFlush(stdout, "converter: %s: attempting %s %s %s\n",
359                time_string.c_str(),
360                missing_info.debug_file.c_str(),
361                missing_info.debug_identifier.c_str(),
362                missing_info.version.c_str());
363 
364   // The first lookup is always to internal symbol servers.
365   // Always ask the symbol servers identified as "full."
366   vector<string> msss_servers = options.full_internal_msss_servers;
367 
368   // If the file is not an .exe file, also ask an additional set of symbol
369   // servers, such as Microsoft's public symbol server.
370   bool is_exe = false;
371 
372   if (missing_info.code_file.length() >= 4) {
373     string code_extension =
374         missing_info.code_file.substr(missing_info.code_file.size() - 4);
375 
376     // Firefox is a special case: .dll-only servers should be consulted for
377     // its symbols.  This enables us to get its symbols from Mozilla's
378     // symbol server when crashes occur in Google extension code hosted by a
379     // Firefox process.
380     if (_stricmp(code_extension.c_str(), ".exe") == 0 &&
381         _stricmp(missing_info.code_file.c_str(), "firefox.exe") != 0) {
382       is_exe = true;
383     }
384   }
385 
386   if (!is_exe) {
387     msss_servers.insert(msss_servers.end(),
388                         options.no_exe_internal_msss_servers.begin(),
389                         options.no_exe_internal_msss_servers.end());
390   }
391 
392   // If there are any suitable internal symbol servers, make a request.
393   MSSymbolServerConverter::LocateResult located =
394       MSSymbolServerConverter::LOCATE_FAILURE;
395   string converted_file;
396   if (msss_servers.size() > 0) {
397     // Attempt to fetch the symbol file and convert it.
398     FprintfFlush(stderr, "Making internal request for %s (%s)\n",
399                    missing_info.debug_file.c_str(),
400                    missing_info.debug_identifier.c_str());
401     MSSymbolServerConverter converter(options.local_cache_path, msss_servers);
402     located = converter.LocateAndConvertSymbolFile(missing_info,
403                                                    false,  // keep_symbol_file
404                                                    false,  // keep_pe_file
405                                                    &converted_file,
406                                                    NULL,   // symbol_file
407                                                    NULL);  // pe_file
408     switch (located) {
409       case MSSymbolServerConverter::LOCATE_SUCCESS:
410         FprintfFlush(stderr, "LocateResult = LOCATE_SUCCESS\n");
411         // Upload it.  Don't bother checking the return value.  If this
412         // succeeds, it should disappear from the missing symbol list.
413         // If it fails, something will print an error message indicating
414         // the cause of the failure, and the item will remain on the
415         // missing symbol list.
416         UploadSymbolFile(options.upload_symbols_url, missing_info,
417                          converted_file);
418         remove(converted_file.c_str());
419 
420         // Note: this does leave some directories behind that could be
421         // cleaned up.  The directories inside options.local_cache_path for
422         // debug_file/debug_identifier can be removed at this point.
423         break;
424 
425       case MSSymbolServerConverter::LOCATE_NOT_FOUND:
426         FprintfFlush(stderr, "LocateResult = LOCATE_NOT_FOUND\n");
427         // The symbol file definitively did not exist. Fall through,
428         // so we can attempt an external query if it's safe to do so.
429         break;
430 
431       case MSSymbolServerConverter::LOCATE_RETRY:
432         FprintfFlush(stderr, "LocateResult = LOCATE_RETRY\n");
433         // Fall through in case we should make an external request.
434         // If not, or if an external request fails in the same way,
435         // we'll leave the entry in the symbol file list and
436         // try again on a future pass.  Print a message so that there's
437         // a record.
438         break;
439 
440       case MSSymbolServerConverter::LOCATE_FAILURE:
441         FprintfFlush(stderr, "LocateResult = LOCATE_FAILURE\n");
442         // LocateAndConvertSymbolFile printed an error message.
443         break;
444 
445       default:
446         FprintfFlush(
447             stderr,
448             "FATAL: Unexpected return value '%d' from "
449             "LocateAndConvertSymbolFile()\n",
450             located);
451         assert(0);
452         break;
453     }
454   } else {
455     // No suitable internal symbol servers.  This is fine because the converter
456     // is mainly used for downloading and converting of external symbols.
457   }
458 
459   // Make a request to an external server if the internal request didn't
460   // succeed, and it's safe to do so.
461   if (located != MSSymbolServerConverter::LOCATE_SUCCESS &&
462       SafeToMakeExternalRequest(missing_info, options.blacklist_regex)) {
463     msss_servers = options.full_external_msss_servers;
464     if (!is_exe) {
465       msss_servers.insert(msss_servers.end(),
466                           options.no_exe_external_msss_servers.begin(),
467                           options.no_exe_external_msss_servers.end());
468     }
469     if (msss_servers.size() > 0) {
470       FprintfFlush(stderr, "Making external request for %s (%s)\n",
471                    missing_info.debug_file.c_str(),
472                    missing_info.debug_identifier.c_str());
473       MSSymbolServerConverter external_converter(options.local_cache_path,
474                                                  msss_servers);
475       located = external_converter.LocateAndConvertSymbolFile(
476           missing_info,
477           false,  // keep_symbol_file
478           false,  // keep_pe_file
479           &converted_file,
480           NULL,   // symbol_file
481           NULL);  // pe_file
482     } else {
483       FprintfFlush(stderr, "ERROR: No suitable external symbol servers.\n");
484     }
485   }
486 
487   // Final handling for this symbol file is based on the result from the
488   // external request (if performed above), or on the result from the
489   // previous internal lookup.
490   switch (located) {
491     case MSSymbolServerConverter::LOCATE_SUCCESS:
492       FprintfFlush(stderr, "LocateResult = LOCATE_SUCCESS\n");
493       // Upload it.  Don't bother checking the return value.  If this
494       // succeeds, it should disappear from the missing symbol list.
495       // If it fails, something will print an error message indicating
496       // the cause of the failure, and the item will remain on the
497       // missing symbol list.
498       UploadSymbolFile(options.upload_symbols_url, missing_info,
499                        converted_file);
500       remove(converted_file.c_str());
501 
502       // Note: this does leave some directories behind that could be
503       // cleaned up.  The directories inside options.local_cache_path for
504       // debug_file/debug_identifier can be removed at this point.
505       break;
506 
507     case MSSymbolServerConverter::LOCATE_NOT_FOUND:
508       // The symbol file definitively didn't exist.  Inform the server.
509       // If this fails, something will print an error message indicating
510       // the cause of the failure, but there's really nothing more to
511       // do.  If this succeeds, the entry should be removed from the
512       // missing symbols list.
513       if (!options.report_fetch_failures) {
514         FprintfFlush(stderr, "SendFetchFailedPing skipped\n");
515       } else if (SendFetchFailedPing(options.fetch_symbol_failure_url,
516                                      missing_info)) {
517         FprintfFlush(stderr, "SendFetchFailedPing succeeded\n");
518       } else {
519         FprintfFlush(stderr, "SendFetchFailedPing failed\n");
520       }
521       break;
522 
523     case MSSymbolServerConverter::LOCATE_RETRY:
524       FprintfFlush(stderr, "LocateResult = LOCATE_RETRY\n");
525       // Nothing to do but leave the entry in the symbol file list and
526       // try again on a future pass.  Print a message so that there's
527       // a record.
528       FprintfFlush(stderr, "ConvertMissingSymbolFile: deferring retry "
529                            "for %s %s %s\n",
530                    missing_info.debug_file.c_str(),
531                    missing_info.debug_identifier.c_str(),
532                    missing_info.version.c_str());
533       break;
534 
535     case MSSymbolServerConverter::LOCATE_FAILURE:
536       FprintfFlush(stderr, "LocateResult = LOCATE_FAILURE\n");
537       // LocateAndConvertSymbolFile printed an error message.
538 
539       // This is due to a bad debug file name, so fetch failed.
540       if (!options.report_fetch_failures) {
541         FprintfFlush(stderr, "SendFetchFailedPing skipped\n");
542       } else if (SendFetchFailedPing(options.fetch_symbol_failure_url,
543                                      missing_info)) {
544         FprintfFlush(stderr, "SendFetchFailedPing succeeded\n");
545       } else {
546         FprintfFlush(stderr, "SendFetchFailedPing failed\n");
547       }
548       break;
549 
550     default:
551       FprintfFlush(
552           stderr,
553           "FATAL: Unexpected return value '%d' from "
554           "LocateAndConvertSymbolFile()\n",
555           located);
556       assert(0);
557       break;
558   }
559 }
560 
561 
562 // Reads the contents of file |file_name| and populates |contents|.
563 // Returns true on success.
ReadFile(string file_name,string * contents)564 static bool ReadFile(string file_name, string *contents) {
565   char buffer[1024 * 8];
566   FILE *fp = fopen(file_name.c_str(), "rt");
567   if (!fp) {
568     return false;
569   }
570   contents->clear();
571   while (fgets(buffer, sizeof(buffer), fp) != NULL) {
572     contents->append(buffer);
573   }
574   fclose(fp);
575   return true;
576 }
577 
578 // ConvertMissingSymbolsList obtains a missing symbol list from
579 // |options.missing_symbols_url| or |options.missing_symbols_file| and calls
580 // ConvertMissingSymbolFile for each missing symbol file in the list.
ConvertMissingSymbolsList(const ConverterOptions & options)581 static bool ConvertMissingSymbolsList(const ConverterOptions &options) {
582   // Set param to indicate requesting for encoded response.
583   map<wstring, wstring> parameters;
584   parameters[L"product"] = L"WinSymConv";
585   parameters[L"encoded"] = L"true";
586   // Get the missing symbol list.
587   string missing_symbol_list;
588   if (!options.missing_symbols_file.empty()) {
589     if (!ReadFile(options.missing_symbols_file, &missing_symbol_list)) {
590       return false;
591     }
592   } else if (!HTTPDownload::Download(options.missing_symbols_url, &parameters,
593                                      &missing_symbol_list, NULL)) {
594     return false;
595   }
596 
597   // Tokenize the content into a vector.
598   vector<string> missing_symbol_lines;
599   Tokenizer::Tokenize("\n", missing_symbol_list, &missing_symbol_lines);
600 
601   FprintfFlush(stderr, "Found %d missing symbol files in list.\n",
602                missing_symbol_lines.size() - 1);  // last line is empty.
603   int convert_attempts = 0;
604   for (vector<string>::const_iterator iterator = missing_symbol_lines.begin();
605        iterator != missing_symbol_lines.end();
606        ++iterator) {
607     // Decode symbol line.
608     const string &encoded_line = *iterator;
609     // Skip lines that are blank.
610     if (encoded_line.empty()) {
611       continue;
612     }
613 
614     string line;
615     if (!WebSafeBase64Unescape(encoded_line, &line)) {
616       // If decoding fails, assume the line is not encoded.
617       // This is helpful when the program connects to a debug server without
618       // encoding.
619       line = encoded_line;
620     }
621 
622     FprintfFlush(stderr, "\nLine: %s\n", line.c_str());
623 
624     // Turn each element into a MissingSymbolInfo structure.
625     MissingSymbolInfo missing_info;
626     if (!ParseMissingString(line, &missing_info)) {
627       FprintfFlush(stderr, "ConvertMissingSymbols: ParseMissingString failed "
628                            "for %s from %ws\n",
629                    line.c_str(), options.missing_symbols_url.c_str());
630       continue;
631     }
632 
633     ++convert_attempts;
634     ConvertMissingSymbolFile(missing_info, options);
635   }
636 
637   // Say something reassuring, since ConvertMissingSymbolFile was never called
638   // and therefore never reported any progress.
639   if (convert_attempts == 0) {
640     string current_time = CurrentDateAndTime();
641     FprintfFlush(stdout, "converter: %s: nothing to convert\n",
642                  current_time.c_str());
643   }
644 
645   return true;
646 }
647 
648 // usage prints the usage message.  It returns 1 as a convenience, to be used
649 // as a return value from main.
usage(const char * program_name)650 static int usage(const char *program_name) {
651   FprintfFlush(stderr,
652       "usage: %s [options]\n"
653       "    -f  <full_msss_server>     MS servers to ask for all symbols\n"
654       "    -n  <no_exe_msss_server>   same, but prevent asking for EXEs\n"
655       "    -l  <local_cache_path>     Temporary local storage for symbols\n"
656       "    -s  <upload_url>           URL for uploading symbols\n"
657       "    -m  <missing_symbols_url>  URL to fetch list of missing symbols\n"
658       "    -mf <missing_symbols_file> File containing the list of missing\n"
659       "                               symbols.  Fetch failures are not\n"
660       "                               reported if such file is provided.\n"
661       "    -t  <fetch_failure_url>    URL to report symbol fetch failure\n"
662       "    -b  <regex>                Regex used to blacklist files to\n"
663       "                               prevent external symbol requests\n"
664       " Note that any server specified by -f or -n that starts with \\filer\n"
665       " will be treated as internal, and all others as external.\n",
666       program_name);
667 
668   return 1;
669 }
670 
671 // "Internal" servers consist only of those whose names start with
672 // the literal string "\\filer\".
IsInternalServer(const string & server_name)673 static bool IsInternalServer(const string &server_name) {
674   if (server_name.find("\\\\filer\\") == 0) {
675     return true;
676   }
677   return false;
678 }
679 
680 // Adds a server with the given name to the list of internal or external
681 // servers, as appropriate.
AddServer(const string & server_name,vector<string> * internal_servers,vector<string> * external_servers)682 static void AddServer(const string &server_name,
683                       vector<string> *internal_servers,
684                       vector<string> *external_servers) {
685   if (IsInternalServer(server_name)) {
686     internal_servers->push_back(server_name);
687   } else {
688     external_servers->push_back(server_name);
689   }
690 }
691 
692 }  // namespace
693 
main(int argc,char ** argv)694 int main(int argc, char **argv) {
695   string time_string = CurrentDateAndTime();
696   FprintfFlush(stdout, "converter: %s: starting\n", time_string.c_str());
697 
698   ConverterOptions options;
699   options.report_fetch_failures = true;
700 
701   // All arguments are paired.
702   if (argc % 2 != 1) {
703     return usage(argv[0]);
704   }
705 
706   string blacklist_regex_str;
707   bool have_any_msss_servers = false;
708   for (int argi = 1; argi < argc; argi += 2) {
709     string option = argv[argi];
710     string value = argv[argi + 1];
711 
712     if (option == "-f") {
713       AddServer(value, &options.full_internal_msss_servers,
714                 &options.full_external_msss_servers);
715       have_any_msss_servers = true;
716     } else if (option == "-n") {
717       AddServer(value, &options.no_exe_internal_msss_servers,
718                 &options.no_exe_external_msss_servers);
719       have_any_msss_servers = true;
720     } else if (option == "-l") {
721       if (!options.local_cache_path.empty()) {
722         return usage(argv[0]);
723       }
724       options.local_cache_path = value;
725     } else if (option == "-s") {
726       if (!WindowsStringUtils::safe_mbstowcs(value,
727                                              &options.upload_symbols_url)) {
728         FprintfFlush(stderr, "main: safe_mbstowcs failed for %s\n",
729                      value.c_str());
730         return 1;
731       }
732     } else if (option == "-m") {
733       if (!WindowsStringUtils::safe_mbstowcs(value,
734                                              &options.missing_symbols_url)) {
735         FprintfFlush(stderr, "main: safe_mbstowcs failed for %s\n",
736                      value.c_str());
737         return 1;
738       }
739     } else if (option == "-mf") {
740       options.missing_symbols_file = value;
741       printf("Getting the list of missing symbols from a file.  Fetch failures"
742              " will not be reported.\n");
743       options.report_fetch_failures = false;
744     } else if (option == "-t") {
745       if (!WindowsStringUtils::safe_mbstowcs(
746           value,
747           &options.fetch_symbol_failure_url)) {
748         FprintfFlush(stderr, "main: safe_mbstowcs failed for %s\n",
749                      value.c_str());
750         return 1;
751       }
752     } else if (option == "-b") {
753       blacklist_regex_str = value;
754     } else {
755       return usage(argv[0]);
756     }
757   }
758 
759   if (blacklist_regex_str.empty()) {
760     FprintfFlush(stderr, "No blacklist specified.\n");
761     return usage(argv[0]);
762   }
763 
764   // Compile the blacklist regular expression for later use.
765   options.blacklist_regex = std::regex(blacklist_regex_str.c_str(),
766       std::regex_constants::icase);
767 
768   // Set the defaults.  If the user specified any MSSS servers, don't use
769   // any default.
770   if (!have_any_msss_servers) {
771     AddServer(kNoExeMSSSServer, &options.no_exe_internal_msss_servers,
772         &options.no_exe_external_msss_servers);
773   }
774 
775   if (options.local_cache_path.empty()) {
776     options.local_cache_path = kLocalCachePath;
777   }
778 
779   if (options.upload_symbols_url.empty()) {
780     FprintfFlush(stderr, "No upload symbols URL specified.\n");
781     return usage(argv[0]);
782   }
783   if (options.missing_symbols_url.empty() &&
784       options.missing_symbols_file.empty()) {
785     FprintfFlush(stderr, "No missing symbols URL or file specified.\n");
786     return usage(argv[0]);
787   }
788   if (options.fetch_symbol_failure_url.empty()) {
789     FprintfFlush(stderr, "No fetch symbol failure URL specified.\n");
790     return usage(argv[0]);
791   }
792 
793   FprintfFlush(stdout,
794                "# of Symbol Servers (int/ext): %d/%d full, %d/%d no_exe\n",
795                options.full_internal_msss_servers.size(),
796                options.full_external_msss_servers.size(),
797                options.no_exe_internal_msss_servers.size(),
798                options.no_exe_external_msss_servers.size());
799 
800   if (!ConvertMissingSymbolsList(options)) {
801     return 1;
802   }
803 
804   time_string = CurrentDateAndTime();
805   FprintfFlush(stdout, "converter: %s: finished\n", time_string.c_str());
806   return 0;
807 }
808