symbol_upload.cc
1 // Copyright 2011 Google LLC 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 LLC 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 // symbol_upload.cc: implemented google_breakpad::sym_upload::Start, a helper 30 // function for linux symbol upload tool. 31 32 #ifdef HAVE_CONFIG_H 33 #include <config.h> // Must come first 34 #endif 35 36 #include "common/linux/symbol_upload.h" 37 38 #include <assert.h> 39 #include <stdio.h> 40 41 #include <functional> 42 #include <iostream> 43 #include <vector> 44 45 #include "common/linux/http_upload.h" 46 #include "common/linux/libcurl_wrapper.h" 47 #include "common/linux/symbol_collector_client.h" 48 49 namespace google_breakpad { 50 namespace sym_upload { 51 52 void TokenizeByChar(const string& source_string, int c, 53 std::vector<string>* results) { 54 assert(results); 55 string::size_type cur_pos = 0, next_pos = 0; 56 while ((next_pos = source_string.find(c, cur_pos)) != string::npos) { 57 if (next_pos != cur_pos) 58 results->push_back(source_string.substr(cur_pos, next_pos - cur_pos)); 59 cur_pos = next_pos + 1; 60 } 61 if (cur_pos < source_string.size() && next_pos != cur_pos) 62 results->push_back(source_string.substr(cur_pos)); 63 } 64 65 //============================================================================= 66 // Parse out the module line which have 5 parts. 67 // MODULE <os> <cpu> <uuid> <module-name> 68 bool ModuleDataForSymbolFile(const string& file, 69 std::vector<string>* module_parts) { 70 assert(module_parts); 71 const size_t kModulePartNumber = 5; 72 FILE* fp = fopen(file.c_str(), "r"); 73 if (fp) { 74 char buffer[1024]; 75 if (fgets(buffer, sizeof(buffer), fp)) { 76 string line(buffer); 77 string::size_type line_break_pos = line.find_first_of('\n'); 78 if (line_break_pos == string::npos) { 79 assert(0 && "The file is invalid!"); 80 fclose(fp); 81 return false; 82 } 83 line.resize(line_break_pos); 84 const char kDelimiter = ' '; 85 TokenizeByChar(line, kDelimiter, module_parts); 86 if (module_parts->size() != kModulePartNumber) 87 module_parts->clear(); 88 } 89 fclose(fp); 90 } 91 92 return module_parts->size() == kModulePartNumber; 93 } 94 95 //============================================================================= 96 string CompactIdentifier(const string& uuid) { 97 std::vector<string> components; 98 TokenizeByChar(uuid, '-', &components); 99 string result; 100 for (size_t i = 0; i < components.size(); ++i) 101 result += components[i]; 102 return result; 103 } 104 105 // |options| describes the current sym_upload options. 106 // |module_parts| contains the strings parsed from the MODULE entry of the 107 // Breakpad symbol file being uploaded. 108 // |compacted_id| is the debug_id from the MODULE entry of the Breakpad symbol 109 // file being uploaded, with all hyphens removed. 110 bool SymUploadV1Start( 111 const Options& options, 112 std::vector<string> module_parts, 113 const string& compacted_id) { 114 std::map<string, string> parameters; 115 // Add parameters 116 if (!options.version.empty()) 117 parameters["version"] = options.version; 118 119 // MODULE <os> <cpu> <uuid> <module-name> 120 // 0 1 2 3 4 121 parameters["os"] = module_parts[1]; 122 parameters["cpu"] = module_parts[2]; 123 parameters["debug_file"] = module_parts[4]; 124 parameters["code_file"] = module_parts[4]; 125 parameters["debug_identifier"] = compacted_id; 126 127 std::map<string, string> files; 128 files["symbol_file"] = options.symbolsPath; 129 130 string response, error; 131 long response_code; 132 bool success = HTTPUpload::SendRequest(options.uploadURLStr, 133 parameters, 134 files, 135 options.proxy, 136 options.proxy_user_pwd, 137 /*ca_certificate_file=*/"", 138 &response, 139 &response_code, 140 &error); 141 142 if (!success) { 143 printf("Failed to send symbol file: %s\n", error.c_str()); 144 printf("Response code: %ld\n", response_code); 145 printf("Response:\n"); 146 printf("%s\n", response.c_str()); 147 } else if (response_code == 0) { 148 printf("Failed to send symbol file: No response code\n"); 149 } else if (response_code != 200) { 150 printf("Failed to send symbol file: Response code %ld\n", response_code); 151 printf("Response:\n"); 152 printf("%s\n", response.c_str()); 153 } else { 154 printf("Successfully sent the symbol file.\n"); 155 } 156 157 return success; 158 } 159 160 // |options| describes the current sym_upload options. 161 // |code_id| is the basename of the module for which symbols are being 162 // uploaded. 163 // |debug_id| is the debug_id of the module for which symbols are being 164 // uploaded. 165 bool SymUploadV2Start( 166 const Options& options, 167 const string& code_file, 168 const string& debug_id, 169 const string& type) { 170 google_breakpad::LibcurlWrapper libcurl_wrapper; 171 if (!libcurl_wrapper.Init()) { 172 printf("Failed to init google_breakpad::LibcurlWrapper.\n"); 173 return false; 174 } 175 176 if (!options.force) { 177 SymbolStatus symbolStatus = SymbolCollectorClient::CheckSymbolStatus( 178 &libcurl_wrapper, 179 options.uploadURLStr, 180 options.api_key, 181 code_file, 182 debug_id); 183 if (symbolStatus == SymbolStatus::Found) { 184 printf("Symbol file already exists, upload aborted." 185 " Use \"-f\" to overwrite.\n"); 186 return true; 187 } else if (symbolStatus == SymbolStatus::Unknown) { 188 printf("Failed to check for existing symbol.\n"); 189 return false; 190 } 191 } 192 193 UploadUrlResponse uploadUrlResponse; 194 if (!SymbolCollectorClient::CreateUploadUrl( 195 &libcurl_wrapper, 196 options.uploadURLStr, 197 options.api_key, 198 &uploadUrlResponse)) { 199 printf("Failed to create upload URL.\n"); 200 return false; 201 } 202 203 string signed_url = uploadUrlResponse.upload_url; 204 string upload_key = uploadUrlResponse.upload_key; 205 string header; 206 string response; 207 long response_code; 208 209 if (!libcurl_wrapper.SendPutRequest(signed_url, 210 options.symbolsPath, 211 &response_code, 212 &header, 213 &response)) { 214 printf("Failed to send symbol file.\n"); 215 printf("Response code: %ld\n", response_code); 216 printf("Response:\n"); 217 printf("%s\n", response.c_str()); 218 return false; 219 } else if (response_code == 0) { 220 printf("Failed to send symbol file: No response code\n"); 221 return false; 222 } else if (response_code != 200) { 223 printf("Failed to send symbol file: Response code %ld\n", response_code); 224 printf("Response:\n"); 225 printf("%s\n", response.c_str()); 226 return false; 227 } 228 229 CompleteUploadResult completeUploadResult = 230 SymbolCollectorClient::CompleteUpload(&libcurl_wrapper, 231 options.uploadURLStr, 232 options.api_key, 233 upload_key, 234 code_file, 235 debug_id, 236 type); 237 if (completeUploadResult == CompleteUploadResult::Error) { 238 printf("Failed to complete upload.\n"); 239 return false; 240 } else if (completeUploadResult == CompleteUploadResult::DuplicateData) { 241 printf("Uploaded file checksum matched existing file checksum," 242 " no change necessary.\n"); 243 } else { 244 printf("Successfully sent the symbol file.\n"); 245 } 246 247 return true; 248 } 249 250 //============================================================================= 251 void Start(Options* options) { 252 if (options->upload_protocol == UploadProtocol::SYM_UPLOAD_V2) { 253 string code_file; 254 string debug_id; 255 string type; 256 257 if (options->type.empty() || options->type == kBreakpadSymbolType) { 258 // Breakpad upload so read these from input file. 259 std::vector<string> module_parts; 260 if (!ModuleDataForSymbolFile(options->symbolsPath, &module_parts)) { 261 fprintf(stderr, "Failed to parse symbol file!\n"); 262 return; 263 } 264 code_file = module_parts[4]; 265 debug_id = CompactIdentifier(module_parts[3]); 266 type = kBreakpadSymbolType; 267 } else { 268 // Native upload so these must be explicitly set. 269 code_file = options->code_file; 270 debug_id = options->debug_id; 271 type = options->type; 272 } 273 274 options->success = SymUploadV2Start(*options, code_file, debug_id, type); 275 } else { 276 std::vector<string> module_parts; 277 if (!ModuleDataForSymbolFile(options->symbolsPath, &module_parts)) { 278 fprintf(stderr, "Failed to parse symbol file!\n"); 279 return; 280 } 281 const string compacted_id = CompactIdentifier(module_parts[3]); 282 options->success = SymUploadV1Start(*options, module_parts, compacted_id); 283 } 284 } 285 286 } // namespace sym_upload 287 } // namespace google_breakpad