Add optional new symbol upload API to sym_upload.

Change-Id: I6a49e9f4a699fa6f5f8e9f0fc86afb4cb342a442
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/1422400
Reviewed-by: Mark Mentovai <mark@chromium.org>
Reviewed-by: Ivan Penkov <ivanpe@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
This commit is contained in:
Nelson Billing 2020-02-19 15:32:08 -08:00
parent 216cea7bca
commit bbad9f255d
11 changed files with 880 additions and 99 deletions

View file

@ -38,32 +38,24 @@
namespace google_breakpad {
LibcurlWrapper::LibcurlWrapper()
: init_ok_(false),
formpost_(NULL),
lastptr_(NULL),
headerlist_(NULL) {
curl_lib_ = dlopen("libcurl.so", RTLD_NOW);
if (!curl_lib_) {
curl_lib_ = dlopen("libcurl.so.4", RTLD_NOW);
}
if (!curl_lib_) {
curl_lib_ = dlopen("libcurl.so.3", RTLD_NOW);
}
if (!curl_lib_) {
std::cout << "Could not find libcurl via dlopen";
return;
}
std::cout << "LibcurlWrapper init succeeded";
init_ok_ = true;
return;
}
curl_lib_(nullptr),
last_curl_error_(""),
curl_(nullptr),
formpost_(nullptr),
lastptr_(nullptr),
headerlist_(nullptr) {}
LibcurlWrapper::~LibcurlWrapper() {}
LibcurlWrapper::~LibcurlWrapper() {
if (init_ok_) {
(*easy_cleanup_)(curl_);
dlclose(curl_lib_);
}
}
bool LibcurlWrapper::SetProxy(const string& proxy_host,
const string& proxy_userpwd) {
if (!init_ok_) {
return false;
}
if (!CheckInit()) return false;
// Set proxy information if necessary.
if (!proxy_host.empty()) {
(*easy_setopt_)(curl_, CURLOPT_PROXY, proxy_host.c_str());
@ -83,9 +75,8 @@ bool LibcurlWrapper::SetProxy(const string& proxy_host,
bool LibcurlWrapper::AddFile(const string& upload_file_path,
const string& basename) {
if (!init_ok_) {
return false;
}
if (!CheckInit()) return false;
std::cout << "Adding " << upload_file_path << " to form upload.";
// Add form file.
(*formadd_)(&formpost_, &lastptr_,
@ -110,10 +101,11 @@ static size_t WriteCallback(void *ptr, size_t size,
bool LibcurlWrapper::SendRequest(const string& url,
const std::map<string, string>& parameters,
int* http_status_code,
long* http_status_code,
string* http_header_data,
string* http_response_data) {
(*easy_setopt_)(curl_, CURLOPT_URL, url.c_str());
if (!CheckInit()) return false;
std::map<string, string>::const_iterator iter = parameters.begin();
for (; iter != parameters.end(); ++iter)
(*formadd_)(&formpost_, &lastptr_,
@ -122,55 +114,79 @@ bool LibcurlWrapper::SendRequest(const string& url,
CURLFORM_END);
(*easy_setopt_)(curl_, CURLOPT_HTTPPOST, formpost_);
if (http_response_data != NULL) {
http_response_data->clear();
(*easy_setopt_)(curl_, CURLOPT_WRITEFUNCTION, WriteCallback);
(*easy_setopt_)(curl_, CURLOPT_WRITEDATA,
reinterpret_cast<void *>(http_response_data));
}
if (http_header_data != NULL) {
http_header_data->clear();
(*easy_setopt_)(curl_, CURLOPT_HEADERFUNCTION, WriteCallback);
(*easy_setopt_)(curl_, CURLOPT_HEADERDATA,
reinterpret_cast<void *>(http_header_data));
return SendRequestInner(url, http_status_code, http_header_data,
http_response_data);
}
bool LibcurlWrapper::SendGetRequest(const string& url,
long* http_status_code,
string* http_header_data,
string* http_response_data) {
if (!CheckInit()) return false;
(*easy_setopt_)(curl_, CURLOPT_HTTPGET, 1L);
return SendRequestInner(url, http_status_code, http_header_data,
http_response_data);
}
bool LibcurlWrapper::SendPutRequest(const string& url,
const string& path,
long* http_status_code,
string* http_header_data,
string* http_response_data) {
if (!CheckInit()) return false;
FILE* file = fopen(path.c_str(), "rb");
(*easy_setopt_)(curl_, CURLOPT_UPLOAD, 1L);
(*easy_setopt_)(curl_, CURLOPT_PUT, 1L);
(*easy_setopt_)(curl_, CURLOPT_READDATA, file);
bool success = SendRequestInner(url, http_status_code, http_header_data,
http_response_data);
fclose(file);
return success;
}
bool LibcurlWrapper::SendSimplePostRequest(const string& url,
const string& body,
const string& content_type,
long* http_status_code,
string* http_header_data,
string* http_response_data) {
if (!CheckInit()) return false;
(*easy_setopt_)(curl_, CURLOPT_POSTFIELDSIZE, body.size());
(*easy_setopt_)(curl_, CURLOPT_COPYPOSTFIELDS, body.c_str());
if (!content_type.empty()) {
string content_type_header = "Content-Type: " + content_type;
headerlist_ = (*slist_append_)(
headerlist_,
content_type_header.c_str());
}
CURLcode err_code = CURLE_OK;
err_code = (*easy_perform_)(curl_);
easy_strerror_ = reinterpret_cast<const char* (*)(CURLcode)>
(dlsym(curl_lib_, "curl_easy_strerror"));
if (http_status_code != NULL) {
(*easy_getinfo_)(curl_, CURLINFO_RESPONSE_CODE, http_status_code);
}
#ifndef NDEBUG
if (err_code != CURLE_OK)
fprintf(stderr, "Failed to send http request to %s, error: %s\n",
url.c_str(),
(*easy_strerror_)(err_code));
#endif
if (headerlist_ != NULL) {
(*slist_free_all_)(headerlist_);
}
(*easy_cleanup_)(curl_);
if (formpost_ != NULL) {
(*formfree_)(formpost_);
}
return err_code == CURLE_OK;
return SendRequestInner(url, http_status_code, http_header_data,
http_response_data);
}
bool LibcurlWrapper::Init() {
if (!init_ok_) {
std::cout << "Init_OK was not true in LibcurlWrapper::Init(), check earlier log messages";
curl_lib_ = dlopen("libcurl.so", RTLD_NOW);
if (!curl_lib_) {
curl_lib_ = dlopen("libcurl.so.4", RTLD_NOW);
}
if (!curl_lib_) {
curl_lib_ = dlopen("libcurl.so.3", RTLD_NOW);
}
if (!curl_lib_) {
std::cout << "Could not find libcurl via dlopen";
return false;
}
if (!SetFunctionPointers()) {
std::cout << "Could not find function pointers";
init_ok_ = false;
return false;
}
@ -184,11 +200,7 @@ bool LibcurlWrapper::Init() {
return false;
}
// Disable 100-continue header.
char buf[] = "Expect:";
headerlist_ = (*slist_append_)(headerlist_, buf);
(*easy_setopt_)(curl_, CURLOPT_HTTPHEADER, headerlist_);
init_ok_ = true;
return true;
}
@ -228,6 +240,10 @@ bool LibcurlWrapper::SetFunctionPointers() {
"curl_easy_getinfo",
CURLcode(*)(CURL *, CURLINFO info, ...));
SET_AND_CHECK_FUNCTION_POINTER(easy_reset_,
"curl_easy_reset",
void(*)(CURL*));
SET_AND_CHECK_FUNCTION_POINTER(slist_free_all_,
"curl_slist_free_all",
void(*)(curl_slist*));
@ -238,4 +254,73 @@ bool LibcurlWrapper::SetFunctionPointers() {
return true;
}
bool LibcurlWrapper::SendRequestInner(const string& url,
long* http_status_code,
string* http_header_data,
string* http_response_data) {
string url_copy(url);
(*easy_setopt_)(curl_, CURLOPT_URL, url_copy.c_str());
// Disable 100-continue header.
char buf[] = "Expect:";
headerlist_ = (*slist_append_)(headerlist_, buf);
(*easy_setopt_)(curl_, CURLOPT_HTTPHEADER, headerlist_);
if (http_response_data != nullptr) {
http_response_data->clear();
(*easy_setopt_)(curl_, CURLOPT_WRITEFUNCTION, WriteCallback);
(*easy_setopt_)(curl_, CURLOPT_WRITEDATA,
reinterpret_cast<void*>(http_response_data));
}
if (http_header_data != nullptr) {
http_header_data->clear();
(*easy_setopt_)(curl_, CURLOPT_HEADERFUNCTION, WriteCallback);
(*easy_setopt_)(curl_, CURLOPT_HEADERDATA,
reinterpret_cast<void*>(http_header_data));
}
CURLcode err_code = CURLE_OK;
err_code = (*easy_perform_)(curl_);
easy_strerror_ = reinterpret_cast<const char* (*)(CURLcode)>
(dlsym(curl_lib_, "curl_easy_strerror"));
if (http_status_code != nullptr) {
(*easy_getinfo_)(curl_, CURLINFO_RESPONSE_CODE, http_status_code);
}
#ifndef NDEBUG
if (err_code != CURLE_OK)
fprintf(stderr, "Failed to send http request to %s, error: %s\n",
url.c_str(),
(*easy_strerror_)(err_code));
#endif
Reset();
return err_code == CURLE_OK;
}
void LibcurlWrapper::Reset() {
if (headerlist_ != nullptr) {
(*slist_free_all_)(headerlist_);
headerlist_ = nullptr;
}
if (formpost_ != nullptr) {
(*formfree_)(formpost_);
formpost_ = nullptr;
}
(*easy_reset_)(curl_);
}
bool LibcurlWrapper::CheckInit() {
if (!init_ok_) {
std::cout << "LibcurlWrapper: You must call Init(), and have it return "
"'true' before invoking any other methods.\n";
return false;
}
return true;
}
} // namespace google_breakpad

View file

@ -51,14 +51,39 @@ class LibcurlWrapper {
const string& basename);
virtual bool SendRequest(const string& url,
const std::map<string, string>& parameters,
int* http_status_code,
long* http_status_code,
string* http_header_data,
string* http_response_data);
bool SendGetRequest(const string& url,
long* http_status_code,
string* http_header_data,
string* http_response_data);
bool SendPutRequest(const string& url,
const string& path,
long* http_status_code,
string* http_header_data,
string* http_response_data);
bool SendSimplePostRequest(const string& url,
const string& body,
const string& content_type,
long* http_status_code,
string* http_header_data,
string* http_response_data);
private:
// This function initializes class state corresponding to function
// pointers into the CURL library.
bool SetFunctionPointers();
bool SendRequestInner(const string& url,
long* http_status_code,
string* http_header_data,
string* http_response_data);
void Reset();
bool CheckInit();
bool init_ok_; // Whether init succeeded
void* curl_lib_; // Pointer to result of dlopen() on
// curl library
@ -85,6 +110,7 @@ class LibcurlWrapper {
const char* (*easy_strerror_)(CURLcode);
void (*easy_cleanup_)(CURL *);
CURLcode (*easy_getinfo_)(CURL *, CURLINFO info, ...);
void (*easy_reset_)(CURL*);
void (*formfree_)(struct curl_httppost *);
};

View file

@ -0,0 +1,193 @@
// Copyright (c) 2019 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "common/linux/symbol_collector_client.h"
#include <stdio.h>
#include <iostream>
#include <regex>
#include "common/linux/libcurl_wrapper.h"
namespace google_breakpad {
namespace sym_upload {
// static
bool SymbolCollectorClient::CreateUploadUrl(
LibcurlWrapper* libcurl_wrapper,
const string& api_url,
const string& api_key,
UploadUrlResponse* uploadUrlResponse) {
string header, response;
long response_code;
string url = api_url + "/v1/uploads:create";
if (!api_key.empty()) {
url += "?key=" + api_key;
}
if (!libcurl_wrapper->SendSimplePostRequest(url,
/*body=*/"",
/*content_type=*/"",
&response_code,
&header,
&response)) {
printf("Failed to create upload url.\n");
printf("Response code: %ld\n", response_code);
printf("Response:\n");
printf("%s\n", response.c_str());
return false;
}
// Note camel-case rather than underscores.
std::regex upload_url_regex("\"uploadUrl\": \"([^\"]+)\"");
std::regex upload_key_regex("\"uploadKey\": \"([^\"]+)\"");
std::smatch upload_url_match;
if (!std::regex_search(response, upload_url_match, upload_url_regex) ||
upload_url_match.size() != 2) {
printf("Failed to parse create url response.");
printf("Response:\n");
printf("%s\n", response.c_str());
return false;
}
string upload_url = upload_url_match[1].str();
std::smatch upload_key_match;
if (!std::regex_search(response, upload_key_match, upload_key_regex) ||
upload_key_match.size() != 2) {
printf("Failed to parse create url response.");
printf("Response:\n");
printf("%s\n", response.c_str());
return false;
}
string upload_key = upload_key_match[1].str();
uploadUrlResponse->upload_url = upload_url;
uploadUrlResponse->upload_key = upload_key;
return true;
}
// static
CompleteUploadResult SymbolCollectorClient::CompleteUpload(
LibcurlWrapper* libcurl_wrapper,
const string& api_url,
const string& api_key,
const string& upload_key,
const string& debug_file,
const string& debug_id) {
string header, response;
long response_code;
string url = api_url + "/v1/uploads/" + upload_key + ":complete";
if (!api_key.empty()) {
url += "?key=" + api_key;
}
string body =
"{ symbol_id: {"
"debug_file: \"" + debug_file + "\", "
"debug_id: \"" + debug_id + "\" } }";
if (!libcurl_wrapper->SendSimplePostRequest(url,
body,
"application/son",
&response_code,
&header,
&response)) {
printf("Failed to complete upload.\n");
printf("Response code: %ld\n", response_code);
printf("Response:\n");
printf("%s\n", response.c_str());
return CompleteUploadResult::Error;
}
std::regex result_regex("\"result\": \"([^\"]+)\"");
std::smatch result_match;
if (!std::regex_search(response, result_match, result_regex) ||
result_match.size() != 2) {
printf("Failed to parse complete upload response.");
printf("Response:\n");
printf("%s\n", response.c_str());
return CompleteUploadResult::Error;
}
string result = result_match[1].str();
if (result.compare("DUPLICATE_DATA") == 0) {
return CompleteUploadResult::DuplicateData;
}
return CompleteUploadResult::Ok;
}
// static
SymbolStatus SymbolCollectorClient::CheckSymbolStatus(
LibcurlWrapper* libcurl_wrapper,
const string& api_url,
const string& api_key,
const string& debug_file,
const string& debug_id) {
string header, response;
long response_code;
string url = api_url +
"/v1/symbols/" + debug_file + "/" + debug_id + ":checkStatus";
if (!api_key.empty()) {
url += "?key=" + api_key;
}
if (!libcurl_wrapper->SendGetRequest(
url,
&response_code,
&header,
&response)) {
printf("Failed to check symbol status, error message.\n");
printf("Response code: %ld\n", response_code);
printf("Response:\n");
printf("%s\n", response.c_str());
return SymbolStatus::Unknown;
}
std::regex status_regex("\"status\": \"([^\"]+)\"");
std::smatch status_match;
if (!std::regex_search(response, status_match, status_regex) ||
status_match.size() != 2) {
printf("Failed to parse check symbol status response.");
printf("Response:\n");
printf("%s\n", response.c_str());
return SymbolStatus::Unknown;
}
string status = status_match[1].str();
return (status.compare("FOUND") == 0) ?
SymbolStatus::Found :
SymbolStatus::Missing;
}
} // namespace sym_upload
} // namespace google_breakpad

View file

@ -0,0 +1,87 @@
// Copyright (c) 2019, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef COMMON_LINUX_SYMBOL_COLLECTOR_CLIENT_H_
#define COMMON_LINUX_SYMBOL_COLLECTOR_CLIENT_H_
#include <string>
#include "common/linux/libcurl_wrapper.h"
#include "common/using_std_string.h"
namespace google_breakpad {
namespace sym_upload {
struct UploadUrlResponse {
string upload_url;
string upload_key;
};
enum SymbolStatus {
Found,
Missing,
Unknown
};
enum CompleteUploadResult {
Ok,
DuplicateData,
Error
};
// Helper class to communicate with a sym-upload-v2 service over HTTP/REST,
// via libcurl.
class SymbolCollectorClient {
public:
static bool CreateUploadUrl(
LibcurlWrapper* libcurl_wrapper,
const string& api_url,
const string& api_key,
UploadUrlResponse* uploadUrlResponse);
static CompleteUploadResult CompleteUpload(
LibcurlWrapper* libcurl_wrapper,
const string& api_url,
const string& api_key,
const string& upload_key,
const string& debug_file,
const string& debug_id);
static SymbolStatus CheckSymbolStatus(
LibcurlWrapper* libcurl_wrapper,
const string& api_url,
const string& api_key,
const string& debug_file,
const string& debug_id);
};
} // namespace sym_upload
} // namespace google_breakpad
#endif // COMMON_LINUX_SYMBOL_COLLECTOR_CLIENT_H_

View file

@ -30,15 +30,19 @@
// symbol_upload.cc: implemented google_breakpad::sym_upload::Start, a helper
// function for linux symbol upload tool.
#include "common/linux/http_upload.h"
#include "common/linux/symbol_upload.h"
#include <assert.h>
#include <stdio.h>
#include <functional>
#include <iostream>
#include <vector>
#include "common/linux/http_upload.h"
#include "common/linux/libcurl_wrapper.h"
#include "common/linux/symbol_collector_client.h"
namespace google_breakpad {
namespace sym_upload {
@ -95,21 +99,19 @@ string CompactIdentifier(const string &uuid) {
return result;
}
//=============================================================================
void Start(Options *options) {
// |options| describes the current sym_upload options.
// |module_parts| contains the strings parsed from the MODULE entry of the
// Breakpad symbol file being uploaded.
// |compacted_id| is the debug_id from the MODULE entry of the Breakpad symbol
// file being uploaded, with all hyphens removed.
bool SymUploadV1Start(
const Options& options,
std::vector<string> module_parts,
const string& compacted_id) {
std::map<string, string> parameters;
options->success = false;
std::vector<string> module_parts;
if (!ModuleDataForSymbolFile(options->symbolsPath, &module_parts)) {
fprintf(stderr, "Failed to parse symbol file!\n");
return;
}
string compacted_id = CompactIdentifier(module_parts[3]);
// Add parameters
if (!options->version.empty())
parameters["version"] = options->version;
if (!options.version.empty())
parameters["version"] = options.version;
// MODULE <os> <cpu> <uuid> <module-name>
// 0 1 2 3 4
@ -120,16 +122,16 @@ void Start(Options *options) {
parameters["debug_identifier"] = compacted_id;
std::map<string, string> files;
files["symbol_file"] = options->symbolsPath;
files["symbol_file"] = options.symbolsPath;
string response, error;
long response_code;
bool success = HTTPUpload::SendRequest(options->uploadURLStr,
bool success = HTTPUpload::SendRequest(options.uploadURLStr,
parameters,
files,
options->proxy,
options->proxy_user_pwd,
"",
options.proxy,
options.proxy_user_pwd,
/*ca_certificate_file=*/"",
&response,
&response_code,
&error);
@ -148,7 +150,116 @@ void Start(Options *options) {
} else {
printf("Successfully sent the symbol file.\n");
}
options->success = success;
return success;
}
// |options| describes the current sym_upload options.
// |module_parts| contains the strings parsed from the MODULE entry of the
// Breakpad symbol file being uploaded.
// |compacted_id| is the debug_id from the MODULE entry of the Breakpad symbol
// file being uploaded, with all hyphens removed.
bool SymUploadV2Start(
const Options& options,
std::vector<string> module_parts,
const string& compacted_id) {
string debug_file = module_parts[4];
string debug_id = compacted_id;
google_breakpad::LibcurlWrapper libcurl_wrapper;
if (!libcurl_wrapper.Init()) {
printf("Failed to init google_breakpad::LibcurlWrapper.\n");
return false;
}
if (!options.force) {
SymbolStatus symbolStatus = SymbolCollectorClient::CheckSymbolStatus(
&libcurl_wrapper,
options.uploadURLStr,
options.api_key,
debug_file,
debug_id);
if (symbolStatus == SymbolStatus::Found) {
printf("Symbol file already exists, upload aborted."
" Use \"-f\" to overwrite.\n");
return true;
} else if (symbolStatus == SymbolStatus::Unknown) {
printf("Failed to check for existing symbol.\n");
return false;
}
}
UploadUrlResponse uploadUrlResponse;
if (!SymbolCollectorClient::CreateUploadUrl(
&libcurl_wrapper,
options.uploadURLStr,
options.api_key,
&uploadUrlResponse)) {
printf("Failed to create upload URL.\n");
return false;
}
string signed_url = uploadUrlResponse.upload_url;
string upload_key = uploadUrlResponse.upload_key;
string header;
string response;
long response_code;
if (!libcurl_wrapper.SendPutRequest(signed_url,
options.symbolsPath,
&response_code,
&header,
&response)) {
printf("Failed to send symbol file.\n");
printf("Response code: %ld\n", response_code);
printf("Response:\n");
printf("%s\n", response.c_str());
return false;
} else if (response_code == 0) {
printf("Failed to send symbol file: No response code\n");
return false;
} else if (response_code != 200) {
printf("Failed to send symbol file: Response code %ld\n", response_code);
printf("Response:\n");
printf("%s\n", response.c_str());
return false;
}
CompleteUploadResult completeUploadResult =
SymbolCollectorClient::CompleteUpload(&libcurl_wrapper,
options.uploadURLStr,
options.api_key,
upload_key,
debug_file,
debug_id);
if (completeUploadResult == CompleteUploadResult::Error) {
printf("Failed to complete upload.\n");
return false;
} else if (completeUploadResult == CompleteUploadResult::DuplicateData) {
printf("Uploaded file checksum matched existing file checksum,"
" no change necessary.\n");
} else {
printf("Successfully sent the symbol file.\n");
}
return true;
}
//=============================================================================
void Start(Options* options) {
std::vector<string> module_parts;
if (!ModuleDataForSymbolFile(options->symbolsPath, &module_parts)) {
fprintf(stderr, "Failed to parse symbol file!\n");
return;
}
const string compacted_id = CompactIdentifier(module_parts[3]);
if (options->upload_protocol == UploadProtocol::SYM_UPLOAD_V2) {
options->success = SymUploadV2Start(*options, module_parts, compacted_id);
} else {
options->success = SymUploadV1Start(*options, module_parts, compacted_id);
}
}
} // namespace sym_upload

View file

@ -41,14 +41,24 @@
namespace google_breakpad {
namespace sym_upload {
typedef struct {
enum class UploadProtocol {
SYM_UPLOAD_V1,
SYM_UPLOAD_V2,
};
struct Options {
Options() : upload_protocol(UploadProtocol::SYM_UPLOAD_V1), force(false) {}
string symbolsPath;
string uploadURLStr;
string proxy;
string proxy_user_pwd;
string version;
bool success;
} Options;
UploadProtocol upload_protocol;
bool force;
string api_key;
};
// Starts upload to symbol server with options.
void Start(Options* options);