Home | History | Annotate | Download | only in symupload
      1 // Copyright (c) 2006, Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 //     * Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 //     * Redistributions in binary form must reproduce the above
     11 // copyright notice, this list of conditions and the following disclaimer
     12 // in the documentation and/or other materials provided with the
     13 // distribution.
     14 //     * Neither the name of Google Inc. nor the names of its
     15 // contributors may be used to endorse or promote products derived from
     16 // this software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 // symupload.cc: Upload a symbol file to a HTTP server.  The upload is sent as
     31 // a multipart/form-data POST request with the following parameters:
     32 //  code_file: the basename of the module, e.g. "app"
     33 //  debug_file: the basename of the debugging file, e.g. "app"
     34 //  debug_identifier: the debug file's identifier, usually consisting of
     35 //                    the guid and age embedded in the pdb, e.g.
     36 //                    "11111111BBBB3333DDDD555555555555F"
     37 //  version: the file version of the module, e.g. "1.2.3.4"
     38 //  os: the operating system that the module was built for
     39 //  cpu: the CPU that the module was built for
     40 //  symbol_file: the contents of the breakpad-format symbol file
     41 
     42 #include <assert.h>
     43 #include <stdio.h>
     44 #include <stdlib.h>
     45 #include <unistd.h>
     46 
     47 #include <functional>
     48 #include <iostream>
     49 #include <string>
     50 #include <vector>
     51 
     52 #include "common/linux/http_upload.h"
     53 #include "common/using_std_string.h"
     54 
     55 using google_breakpad::HTTPUpload;
     56 
     57 typedef struct {
     58   string symbolsPath;
     59   string uploadURLStr;
     60   string proxy;
     61   string proxy_user_pwd;
     62   string version;
     63   bool success;
     64 } Options;
     65 
     66 static void TokenizeByChar(const string &source_string,
     67               int c, std::vector<string> *results) {
     68   assert(results);
     69   string::size_type cur_pos = 0, next_pos = 0;
     70   while ((next_pos = source_string.find(c, cur_pos)) != string::npos) {
     71     if (next_pos != cur_pos)
     72       results->push_back(source_string.substr(cur_pos, next_pos - cur_pos));
     73     cur_pos = next_pos + 1;
     74   }
     75   if (cur_pos < source_string.size() && next_pos != cur_pos)
     76     results->push_back(source_string.substr(cur_pos));
     77 }
     78 
     79 //=============================================================================
     80 // Parse out the module line which have 5 parts.
     81 // MODULE <os> <cpu> <uuid> <module-name>
     82 static bool ModuleDataForSymbolFile(const string &file,
     83                                     std::vector<string> *module_parts) {
     84   assert(module_parts);
     85   const size_t kModulePartNumber = 5;
     86   FILE* fp = fopen(file.c_str(), "r");
     87   if (fp) {
     88     char buffer[1024];
     89     if (fgets(buffer, sizeof(buffer), fp)) {
     90       string line(buffer);
     91       string::size_type line_break_pos = line.find_first_of('\n');
     92       if (line_break_pos == string::npos) {
     93         assert(0 && "The file is invalid!");
     94         fclose(fp);
     95         return false;
     96       }
     97       line.resize(line_break_pos);
     98       const char kDelimiter = ' ';
     99       TokenizeByChar(line, kDelimiter, module_parts);
    100       if (module_parts->size() != kModulePartNumber)
    101         module_parts->clear();
    102     }
    103     fclose(fp);
    104   }
    105 
    106   return module_parts->size() == kModulePartNumber;
    107 }
    108 
    109 //=============================================================================
    110 static string CompactIdentifier(const string &uuid) {
    111   std::vector<string> components;
    112   TokenizeByChar(uuid, '-', &components);
    113   string result;
    114   for (size_t i = 0; i < components.size(); ++i)
    115     result += components[i];
    116   return result;
    117 }
    118 
    119 //=============================================================================
    120 static void Start(Options *options) {
    121   std::map<string, string> parameters;
    122   options->success = false;
    123   std::vector<string> module_parts;
    124   if (!ModuleDataForSymbolFile(options->symbolsPath, &module_parts)) {
    125     fprintf(stderr, "Failed to parse symbol file!\n");
    126     return;
    127   }
    128 
    129   string compacted_id = CompactIdentifier(module_parts[3]);
    130 
    131   // Add parameters
    132   if (!options->version.empty())
    133     parameters["version"] = options->version;
    134 
    135   // MODULE <os> <cpu> <uuid> <module-name>
    136   // 0      1    2     3      4
    137   parameters["os"] = module_parts[1];
    138   parameters["cpu"] = module_parts[2];
    139   parameters["debug_file"] = module_parts[4];
    140   parameters["code_file"] = module_parts[4];
    141   parameters["debug_identifier"] = compacted_id;
    142   string response, error;
    143   long response_code;
    144   bool success = HTTPUpload::SendRequest(options->uploadURLStr,
    145                                          parameters,
    146                                          options->symbolsPath,
    147                                          "symbol_file",
    148                                          options->proxy,
    149                                          options->proxy_user_pwd,
    150                                          "",
    151                                          &response,
    152                                          &response_code,
    153                                          &error);
    154 
    155   if (!success) {
    156     printf("Failed to send symbol file: %s\n", error.c_str());
    157     printf("Response code: %ld\n", response_code);
    158     printf("Response:\n");
    159     printf("%s\n", response.c_str());
    160   } else if (response_code == 0) {
    161     printf("Failed to send symbol file: No response code\n");
    162   } else if (response_code != 200) {
    163     printf("Failed to send symbol file: Response code %ld\n", response_code);
    164     printf("Response:\n");
    165     printf("%s\n", response.c_str());
    166   } else {
    167     printf("Successfully sent the symbol file.\n");
    168   }
    169   options->success = success;
    170 }
    171 
    172 //=============================================================================
    173 static void
    174 Usage(int argc, const char *argv[]) {
    175   fprintf(stderr, "Submit symbol information.\n");
    176   fprintf(stderr, "Usage: %s [options...] <symbols> <upload-URL>\n", argv[0]);
    177   fprintf(stderr, "Options:\n");
    178   fprintf(stderr, "<symbols> should be created by using the dump_syms tool.\n");
    179   fprintf(stderr, "<upload-URL> is the destination for the upload\n");
    180   fprintf(stderr, "-v:\t Version information (e.g., 1.2.3.4)\n");
    181   fprintf(stderr, "-x:\t <host[:port]> Use HTTP proxy on given port\n");
    182   fprintf(stderr, "-u:\t <user[:password]> Set proxy user and password\n");
    183   fprintf(stderr, "-h:\t Usage\n");
    184   fprintf(stderr, "-?:\t Usage\n");
    185 }
    186 
    187 //=============================================================================
    188 static void
    189 SetupOptions(int argc, const char *argv[], Options *options) {
    190   extern int optind;
    191   int ch;
    192 
    193   while ((ch = getopt(argc, (char * const *)argv, "u:v:x:h?")) != -1) {
    194     switch (ch) {
    195       case 'u':
    196         options->proxy_user_pwd = optarg;
    197         break;
    198       case 'v':
    199         options->version = optarg;
    200         break;
    201       case 'x':
    202         options->proxy = optarg;
    203         break;
    204 
    205       default:
    206         fprintf(stderr, "Invalid option '%c'\n", ch);
    207         Usage(argc, argv);
    208         exit(1);
    209         break;
    210     }
    211   }
    212 
    213   if ((argc - optind) != 2) {
    214     fprintf(stderr, "%s: Missing symbols file and/or upload-URL\n", argv[0]);
    215     Usage(argc, argv);
    216     exit(1);
    217   }
    218 
    219   options->symbolsPath = argv[optind];
    220   options->uploadURLStr = argv[optind + 1];
    221 }
    222 
    223 //=============================================================================
    224 int main(int argc, const char* argv[]) {
    225   Options options;
    226   SetupOptions(argc, argv, &options);
    227   Start(&options);
    228   return options.success ? 0 : 1;
    229 }
    230