/ src / common / linux / symbol_upload.cc
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