michael@0: // Copyright (c) 2006, Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: // symupload.cc: Upload a symbol file to a HTTP server. The upload is sent as michael@0: // a multipart/form-data POST request with the following parameters: michael@0: // code_file: the basename of the module, e.g. "app" michael@0: // debug_file: the basename of the debugging file, e.g. "app" michael@0: // debug_identifier: the debug file's identifier, usually consisting of michael@0: // the guid and age embedded in the pdb, e.g. michael@0: // "11111111BBBB3333DDDD555555555555F" michael@0: // version: the file version of the module, e.g. "1.2.3.4" michael@0: // os: the operating system that the module was built for michael@0: // cpu: the CPU that the module was built for michael@0: // symbol_file: the contents of the breakpad-format symbol file michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "common/linux/http_upload.h" michael@0: michael@0: using google_breakpad::HTTPUpload; michael@0: michael@0: typedef struct { michael@0: std::string symbolsPath; michael@0: std::string uploadURLStr; michael@0: std::string proxy; michael@0: std::string proxy_user_pwd; michael@0: std::string version; michael@0: bool success; michael@0: } Options; michael@0: michael@0: static void TokenizeByChar(const std::string &source_string, michael@0: int c, std::vector *results) { michael@0: assert(results); michael@0: std::string::size_type cur_pos = 0, next_pos = 0; michael@0: while ((next_pos = source_string.find(c, cur_pos)) != std::string::npos) { michael@0: if (next_pos != cur_pos) michael@0: results->push_back(source_string.substr(cur_pos, next_pos - cur_pos)); michael@0: cur_pos = next_pos + 1; michael@0: } michael@0: if (cur_pos < source_string.size() && next_pos != cur_pos) michael@0: results->push_back(source_string.substr(cur_pos)); michael@0: } michael@0: michael@0: //============================================================================= michael@0: // Parse out the module line which have 5 parts. michael@0: // MODULE michael@0: static bool ModuleDataForSymbolFile(const std::string &file, michael@0: std::vector *module_parts) { michael@0: assert(module_parts); michael@0: const size_t kModulePartNumber = 5; michael@0: FILE *fp = fopen(file.c_str(), "r"); michael@0: if (fp) { michael@0: char buffer[1024]; michael@0: if (fgets(buffer, sizeof(buffer), fp)) { michael@0: std::string line(buffer); michael@0: std::string::size_type line_break_pos = line.find_first_of('\n'); michael@0: if (line_break_pos == std::string::npos) { michael@0: assert(0 && "The file is invalid!"); michael@0: fclose(fp); michael@0: return false; michael@0: } michael@0: line.resize(line_break_pos); michael@0: const char kDelimiter = ' '; michael@0: TokenizeByChar(line, kDelimiter, module_parts); michael@0: if (module_parts->size() != kModulePartNumber) michael@0: module_parts->clear(); michael@0: } michael@0: fclose(fp); michael@0: } michael@0: michael@0: return module_parts->size() == kModulePartNumber; michael@0: } michael@0: michael@0: //============================================================================= michael@0: static std::string CompactIdentifier(const std::string &uuid) { michael@0: std::vector components; michael@0: TokenizeByChar(uuid, '-', &components); michael@0: std::string result; michael@0: for (size_t i = 0; i < components.size(); ++i) michael@0: result += components[i]; michael@0: return result; michael@0: } michael@0: michael@0: //============================================================================= michael@0: static void Start(Options *options) { michael@0: std::map parameters; michael@0: options->success = false; michael@0: std::vector module_parts; michael@0: if (!ModuleDataForSymbolFile(options->symbolsPath, &module_parts)) { michael@0: fprintf(stderr, "Failed to parse symbol file!\n"); michael@0: return; michael@0: } michael@0: michael@0: std::string compacted_id = CompactIdentifier(module_parts[3]); michael@0: michael@0: // Add parameters michael@0: if (!options->version.empty()) michael@0: parameters["version"] = options->version; michael@0: michael@0: // MODULE michael@0: // 0 1 2 3 4 michael@0: parameters["os"] = module_parts[1]; michael@0: parameters["cpu"] = module_parts[2]; michael@0: parameters["debug_file"] = module_parts[4]; michael@0: parameters["code_file"] = module_parts[4]; michael@0: parameters["debug_identifier"] = compacted_id; michael@0: std::string response, error; michael@0: long response_code; michael@0: bool success = HTTPUpload::SendRequest(options->uploadURLStr, michael@0: parameters, michael@0: options->symbolsPath, michael@0: "symbol_file", michael@0: options->proxy, michael@0: options->proxy_user_pwd, michael@0: "", michael@0: &response, michael@0: &response_code, michael@0: &error); michael@0: michael@0: if (!success) { michael@0: printf("Failed to send symbol file: %s\n", error.c_str()); michael@0: printf("Response:\n"); michael@0: printf("%s\n", response.c_str()); michael@0: } else if (response_code == 0) { michael@0: printf("Failed to send symbol file: No response code\n"); michael@0: } else if (response_code != 200) { michael@0: printf("Failed to send symbol file: Response code %ld\n", response_code); michael@0: printf("Response:\n"); michael@0: printf("%s\n", response.c_str()); michael@0: } else { michael@0: printf("Successfully sent the symbol file.\n"); michael@0: } michael@0: options->success = success; michael@0: } michael@0: michael@0: //============================================================================= michael@0: static void michael@0: Usage(int argc, const char *argv[]) { michael@0: fprintf(stderr, "Submit symbol information.\n"); michael@0: fprintf(stderr, "Usage: %s [options...] \n", argv[0]); michael@0: fprintf(stderr, "Options:\n"); michael@0: fprintf(stderr, " should be created by using the dump_syms tool.\n"); michael@0: fprintf(stderr, " is the destination for the upload\n"); michael@0: fprintf(stderr, "-v:\t Version information (e.g., 1.2.3.4)\n"); michael@0: fprintf(stderr, "-x:\t Use HTTP proxy on given port\n"); michael@0: fprintf(stderr, "-u:\t Set proxy user and password\n"); michael@0: fprintf(stderr, "-h:\t Usage\n"); michael@0: fprintf(stderr, "-?:\t Usage\n"); michael@0: } michael@0: michael@0: //============================================================================= michael@0: static void michael@0: SetupOptions(int argc, const char *argv[], Options *options) { michael@0: extern int optind; michael@0: char ch; michael@0: michael@0: while ((ch = getopt(argc, (char * const *)argv, "u:v:x:h?")) != -1) { michael@0: switch (ch) { michael@0: case 'u': michael@0: options->proxy_user_pwd = optarg; michael@0: break; michael@0: case 'v': michael@0: options->version = optarg; michael@0: break; michael@0: case 'x': michael@0: options->proxy = optarg; michael@0: break; michael@0: michael@0: default: michael@0: Usage(argc, argv); michael@0: exit(0); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if ((argc - optind) != 2) { michael@0: fprintf(stderr, "%s: Missing symbols file and/or upload-URL\n", argv[0]); michael@0: Usage(argc, argv); michael@0: exit(1); michael@0: } michael@0: michael@0: options->symbolsPath = argv[optind]; michael@0: options->uploadURLStr = argv[optind + 1]; michael@0: } michael@0: michael@0: //============================================================================= michael@0: int main (int argc, const char * argv[]) { michael@0: Options options; michael@0: SetupOptions(argc, argv, &options); michael@0: Start(&options); michael@0: return options.success ? 0 : 1; michael@0: }