diff options
author | mmandlis@chromium.org <mmandlis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-27 20:52:06 +0000 |
---|---|---|
committer | mmandlis@chromium.org <mmandlis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-27 20:52:06 +0000 |
commit | b12387e66a2a39bed3faa6a9e4617272cf2ee2d2 (patch) | |
tree | 05afa38280b1e5795aa363065117765645f7b147 | |
parent | 4ca013ad73bd2c6491527a4b5555c41843a34661 (diff) | |
download | chromium_src-b12387e66a2a39bed3faa6a9e4617272cf2ee2d2.zip chromium_src-b12387e66a2a39bed3faa6a9e4617272cf2ee2d2.tar.gz chromium_src-b12387e66a2a39bed3faa6a9e4617272cf2ee2d2.tar.bz2 |
Add upload support to chrome://tracing.
BUG=
Review URL: https://codereview.chromium.org/313733004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@280407 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | content/browser/tracing/DEPS | 1 | ||||
-rw-r--r-- | content/browser/tracing/trace_uploader.cc | 211 | ||||
-rw-r--r-- | content/browser/tracing/trace_uploader.h | 91 | ||||
-rw-r--r-- | content/browser/tracing/tracing_ui.cc | 115 | ||||
-rw-r--r-- | content/browser/tracing/tracing_ui.h | 11 | ||||
-rw-r--r-- | content/content_browser.gypi | 3 | ||||
-rw-r--r-- | content/public/common/content_switches.cc | 2 | ||||
-rw-r--r-- | content/public/common/content_switches.h | 1 |
8 files changed, 433 insertions, 2 deletions
diff --git a/content/browser/tracing/DEPS b/content/browser/tracing/DEPS index b4d8715..b99424f 100644 --- a/content/browser/tracing/DEPS +++ b/content/browser/tracing/DEPS @@ -1,4 +1,5 @@ include_rules = [ # Generated by the local tracing_resources.gyp:tracing_resources "+grit/tracing_resources.h", + "+third_party/zlib/zlib.h", ] diff --git a/content/browser/tracing/trace_uploader.cc b/content/browser/tracing/trace_uploader.cc new file mode 100644 index 0000000..e707249 --- /dev/null +++ b/content/browser/tracing/trace_uploader.cc @@ -0,0 +1,211 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/tracing/trace_uploader.h" + +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/memory/shared_memory.h" +#include "base/path_service.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/time/time.h" +#include "content/public/browser/browser_thread.h" +#include "net/base/mime_util.h" +#include "net/base/network_delegate.h" +#include "net/proxy/proxy_config.h" +#include "net/proxy/proxy_config_service.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" +#include "net/url_request/url_request_context_getter.h" +#include "third_party/zlib/zlib.h" +#include "url/gurl.h" + +namespace content { +namespace { +const char kUploadContentType[] = "multipart/form-data"; +const char kMultipartBoundary[] = + "----**--yradnuoBgoLtrapitluMklaTelgooG--**----"; + +const int kHttpResponseOk = 200; + +} // namespace + +TraceUploader::TraceUploader(const std::string& product, + const std::string& version, + const std::string& upload_url, + net::URLRequestContextGetter* request_context) + : product_(product), + version_(version), + upload_url_(upload_url), + request_context_(request_context) { + DCHECK(!product_.empty()); + DCHECK(!version_.empty()); + DCHECK(!upload_url_.empty()); +} + +TraceUploader::~TraceUploader() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +} + +void TraceUploader::OnURLFetchComplete(const net::URLFetcher* source) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK_EQ(source, url_fetcher_.get()); + int response_code = source->GetResponseCode(); + string report_id; + string error_message; + bool success = (response_code == kHttpResponseOk); + if (success) { + source->GetResponseAsString(&report_id); + } else { + error_message = "Uploading failed, response code: " + + base::IntToString(response_code); + } + + BrowserThread::PostTask( + content::BrowserThread::UI, + FROM_HERE, + base::Bind(done_callback_, success, report_id, error_message)); + url_fetcher_.reset(); +} + +void TraceUploader::OnURLFetchUploadProgress( + const net::URLFetcher* source, int64 current, int64 total) { + DCHECK(url_fetcher_.get()); + + LOG(WARNING) << "Upload progress: " << current << " of " << total; + BrowserThread::PostTask( + content::BrowserThread::UI, + FROM_HERE, + base::Bind(progress_callback_, current, total)); +} + +void TraceUploader::DoUpload( + const std::string& file_contents, + UploadProgressCallback progress_callback, + UploadDoneCallback done_callback) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + DCHECK(!url_fetcher_.get()); + + progress_callback_ = progress_callback; + done_callback_ = done_callback; + + if (url_fetcher_.get()) { + OnUploadError("Already uploading."); + } + + scoped_ptr<char[]> compressed_contents(new char[kMaxUploadBytes]); + int compressed_bytes; + if (!Compress(file_contents, kMaxUploadBytes, compressed_contents.get(), + &compressed_bytes)) { + OnUploadError("Compressing file failed."); + return; + } + + std::string post_data; + SetupMultipart("trace.json.gz", + std::string(compressed_contents.get(), compressed_bytes), + &post_data); + + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&TraceUploader::CreateAndStartURLFetcher, + base::Unretained(this), + post_data)); +} + +void TraceUploader::OnUploadError(std::string error_message) { + LOG(ERROR) << error_message; + content::BrowserThread::PostTask( + content::BrowserThread::UI, + FROM_HERE, + base::Bind(done_callback_, false, "", error_message)); +} + +void TraceUploader::SetupMultipart(const std::string& trace_filename, + const std::string& trace_contents, + std::string* post_data) { + net::AddMultipartValueForUpload("prod", product_, kMultipartBoundary, "", + post_data); + net::AddMultipartValueForUpload("ver", version_ + "-trace", + kMultipartBoundary, "", post_data); + net::AddMultipartValueForUpload("guid", "0", kMultipartBoundary, + "", post_data); + net::AddMultipartValueForUpload("type", "trace", kMultipartBoundary, + "", post_data); + // No minidump means no need for crash to process the report. + net::AddMultipartValueForUpload("should_process", "false", kMultipartBoundary, + "", post_data); + + AddTraceFile(trace_filename, trace_contents, post_data); + + net::AddMultipartFinalDelimiterForUpload(kMultipartBoundary, post_data); +} + +void TraceUploader::AddTraceFile(const std::string& trace_filename, + const std::string& trace_contents, + std::string* post_data) { + post_data->append("--"); + post_data->append(kMultipartBoundary); + post_data->append("\r\n"); + post_data->append("Content-Disposition: form-data; name=\"trace\""); + post_data->append("; filename=\""); + post_data->append(trace_filename); + post_data->append("\"\r\n"); + post_data->append("Content-Type: application/gzip\r\n\r\n"); + post_data->append(trace_contents); + post_data->append("\r\n"); +} + +bool TraceUploader::Compress(std::string input, + int max_compressed_bytes, + char* compressed, + int* compressed_bytes) { + DCHECK(compressed); + DCHECK(compressed_bytes); + z_stream stream = {0}; + int result = deflateInit2(&stream, + Z_DEFAULT_COMPRESSION, + Z_DEFLATED, + // 16 is added to produce a gzip header + trailer. + MAX_WBITS + 16, + 8, // memLevel = 8 is default. + Z_DEFAULT_STRATEGY); + DCHECK_EQ(Z_OK, result); + stream.next_in = reinterpret_cast<uint8*>(&input[0]); + stream.avail_in = input.size(); + stream.next_out = reinterpret_cast<uint8*>(compressed); + stream.avail_out = max_compressed_bytes; + // Do a one-shot compression. This will return Z_STREAM_END only if |output| + // is large enough to hold all compressed data. + result = deflate(&stream, Z_FINISH); + bool success = (result == Z_STREAM_END); + result = deflateEnd(&stream); + DCHECK(result == Z_OK || result == Z_DATA_ERROR); + + if (success) + *compressed_bytes = max_compressed_bytes - stream.avail_out; + + LOG(WARNING) << "input size: " << input.size() + << ", output size: " << *compressed_bytes; + return success; +} + +void TraceUploader::CreateAndStartURLFetcher(const std::string& post_data) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK(!url_fetcher_.get()); + + std::string content_type = kUploadContentType; + content_type.append("; boundary="); + content_type.append(kMultipartBoundary); + + url_fetcher_.reset( + net::URLFetcher::Create(GURL(upload_url_), net::URLFetcher::POST, this)); + url_fetcher_->SetRequestContext(request_context_); + url_fetcher_->SetUploadData(content_type, post_data); + url_fetcher_->Start(); +} + +} // namespace content diff --git a/content/browser/tracing/trace_uploader.h b/content/browser/tracing/trace_uploader.h new file mode 100644 index 0000000..bfdd40f --- /dev/null +++ b/content/browser/tracing/trace_uploader.h @@ -0,0 +1,91 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_TRACING_TRACE_UPLOADER_H_ +#define CONTENT_BROWSER_TRACING_TRACE_UPLOADER_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/gtest_prod_util.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "net/url_request/url_fetcher_delegate.h" + +namespace net { +class URLFetcher; +class URLRequestContextGetter; +} // namespace net + +namespace content { + +namespace { + +// Allow up to 10MB for trace upload +const int kMaxUploadBytes = 10000000; + +} // namespace + +// TraceUploader uploads traces. +class TraceUploader : public net::URLFetcherDelegate { + public: + typedef base::Callback<void(bool, const std::string&, const std::string&)> + UploadDoneCallback; + typedef base::Callback<void(int64, int64)> UploadProgressCallback; + + TraceUploader(const std::string& product, + const std::string& version, + const std::string& upload_url, + net::URLRequestContextGetter* request_context); + virtual ~TraceUploader(); + + // net::URLFetcherDelegate implementation. + virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; + virtual void OnURLFetchUploadProgress(const net::URLFetcher* source, + int64 current, int64 total) OVERRIDE; + + // Compresses and uploads the given file contents. + void DoUpload(const std::string& file_contents, + UploadProgressCallback progress_callback, + UploadDoneCallback done_callback); + + private: + // Sets up a multipart body to be uploaded. The body is produced according + // to RFC 2046. + void SetupMultipart(const std::string& trace_filename, + const std::string& trace_contents, + std::string* post_data); + void AddTraceFile(const std::string& trace_filename, + const std::string& trace_contents, + std::string* post_data); + // Compresses the input and returns whether compression was successful. + bool Compress(std::string input, + int max_compressed_bytes, + char* compressed_contents, + int* compressed_bytes); + void CreateAndStartURLFetcher(const std::string& post_data); + void OnUploadError(std::string error_message); + + std::string product_; + std::string version_; + + std::string upload_url_; + + scoped_ptr<net::URLFetcher> url_fetcher_; + UploadProgressCallback progress_callback_; + UploadDoneCallback done_callback_; + + net::URLRequestContextGetter* request_context_; + + DISALLOW_COPY_AND_ASSIGN(TraceUploader); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_TRACING_TRACE_UPLOADER_H_ diff --git a/content/browser/tracing/tracing_ui.cc b/content/browser/tracing/tracing_ui.cc index 9f31753..3edcd18 100644 --- a/content/browser/tracing/tracing_ui.cc +++ b/content/browser/tracing/tracing_ui.cc @@ -4,33 +4,44 @@ #include "content/browser/tracing/tracing_ui.h" +#include <set> #include <string> +#include <vector> #include "base/base64.h" #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/command_line.h" #include "base/file_util.h" +#include "base/format_macros.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "base/values.h" #include "content/browser/tracing/grit/tracing_resources.h" +#include "content/browser/tracing/trace_uploader.h" #include "content/browser/tracing/tracing_controller_impl.h" +#include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/tracing_controller.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui_data_source.h" +#include "content/public/common/content_client.h" +#include "content/public/common/content_switches.h" #include "content/public/common/url_constants.h" namespace content { namespace { +const char kUploadURL[] = "https://clients2.google.com/cr/staging_report"; + void OnGotCategories(const WebUIDataSource::GotDataCallback& callback, const std::set<std::string>& categorySet) { - scoped_ptr<base::ListValue> category_list(new base::ListValue()); for (std::set<std::string>::const_iterator it = categorySet.begin(); it != categorySet.end(); it++) { @@ -265,7 +276,13 @@ bool OnTracingRequest(const std::string& path, // //////////////////////////////////////////////////////////////////////////////// -TracingUI::TracingUI(WebUI* web_ui) : WebUIController(web_ui) { +TracingUI::TracingUI(WebUI* web_ui) + : WebUIController(web_ui), + weak_factory_(this) { + web_ui->RegisterMessageCallback( + "doUpload", + base::Bind(&TracingUI::DoUpload, base::Unretained(this))); + // Set up the chrome://tracing/ source. BrowserContext* browser_context = web_ui->GetWebContents()->GetBrowserContext(); @@ -288,4 +305,98 @@ void TracingUI::OnMonitoringStateChanged(bool is_monitoring) { "onMonitoringStateChanged", base::FundamentalValue(is_monitoring)); } +void TracingUI::DoUpload(const base::ListValue* args) { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + std::string upload_url = kUploadURL; + if (command_line.HasSwitch(switches::kTraceUploadURL)) { + upload_url = + command_line.GetSwitchValueASCII(switches::kTraceUploadURL); + } + if (!GURL(upload_url).is_valid()) { + upload_url.clear(); + } + + if (upload_url.empty()) { + web_ui()->CallJavascriptFunction("onUploadError", + base::StringValue("Upload URL empty or invalid")); + return; + } + + std::string file_contents; + if (!args || args->empty() || !args->GetString(0, &file_contents)) { + web_ui()->CallJavascriptFunction("onUploadError", + base::StringValue("Missing data")); + return; + } + + TraceUploader::UploadProgressCallback progress_callback = + base::Bind(&TracingUI::OnTraceUploadProgress, + weak_factory_.GetWeakPtr()); + TraceUploader::UploadDoneCallback done_callback = + base::Bind(&TracingUI::OnTraceUploadComplete, + weak_factory_.GetWeakPtr()); + +#if defined(OS_WIN) + const char product[] = "Chrome"; +#elif defined(OS_MACOSX) + const char product[] = "Chrome_Mac"; +#elif defined(OS_LINUX) + const char product[] = "Chrome_Linux"; +#elif defined(OS_ANDROID) + const char product[] = "Chrome_Android"; +#elif defined(OS_CHROMEOS) + const char product[] = "Chrome_ChromeOS"; +#else +#error Platform not supported. +#endif + + // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out + // the part before the "/". + std::vector<std::string> product_components; + base::SplitString(content::GetContentClient()->GetProduct(), '/', + &product_components); + DCHECK_EQ(2U, product_components.size()); + std::string version; + if (product_components.size() == 2U) { + version = product_components[1]; + } else { + version = "unknown"; + } + + BrowserContext* browser_context = + web_ui()->GetWebContents()->GetBrowserContext(); + TraceUploader* uploader = new TraceUploader( + product, version, upload_url, browser_context->GetRequestContext()); + + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( + &TraceUploader::DoUpload, + base::Unretained(uploader), + file_contents, + progress_callback, + done_callback)); + // TODO(mmandlis): Add support for stopping the upload in progress. +} + +void TracingUI::OnTraceUploadProgress(int64 current, int64 total) { + DCHECK(current <= total); + int percent = (current / total) * 100; + web_ui()->CallJavascriptFunction( + "onUploadProgress", + base::FundamentalValue(percent), + base::StringValue(base::StringPrintf("%" PRId64, current)), + base::StringValue(base::StringPrintf("%" PRId64, total))); +} + +void TracingUI::OnTraceUploadComplete(bool success, + const std::string& report_id, + const std::string& error_message) { + if (success) { + web_ui()->CallJavascriptFunction("onUploadComplete", + base::StringValue(report_id)); + } else { + web_ui()->CallJavascriptFunction("onUploadError", + base::StringValue(error_message)); + } +} + } // namespace content diff --git a/content/browser/tracing/tracing_ui.h b/content/browser/tracing/tracing_ui.h index 40ce082..b0afde0 100644 --- a/content/browser/tracing/tracing_ui.h +++ b/content/browser/tracing/tracing_ui.h @@ -5,6 +5,10 @@ #ifndef CONTENT_BROWSER_TRACING_UI_H_ #define CONTENT_BROWSER_TRACING_UI_H_ +#include <map> +#include <string> + +#include "base/memory/weak_ptr.h" #include "content/public/browser/web_ui_controller.h" namespace content { @@ -15,8 +19,15 @@ class CONTENT_EXPORT TracingUI : public WebUIController { explicit TracingUI(WebUI* web_ui); virtual ~TracingUI(); void OnMonitoringStateChanged(bool is_monitoring); + void DoUpload(const base::ListValue* args); + void OnTraceUploadProgress(int64 current, int64 total); + void OnTraceUploadComplete(bool success, + const std::string& report_id, + const std::string& error_message); private: + base::WeakPtrFactory<TracingUI> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(TracingUI); }; diff --git a/content/content_browser.gypi b/content/content_browser.gypi index d8a00d3..eedb878 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -1240,6 +1240,8 @@ 'browser/tracing/trace_message_filter.h', 'browser/tracing/etw_system_event_consumer_win.cc', 'browser/tracing/etw_system_event_consumer_win.h', + 'browser/tracing/trace_uploader.cc', + 'browser/tracing/trace_uploader.h', 'browser/tracing/tracing_controller_impl.cc', 'browser/tracing/tracing_controller_impl.h', 'browser/tracing/tracing_ui.cc', @@ -1720,6 +1722,7 @@ ['exclude', '^browser/device_sensors/data_fetcher_shared_memory_default\\.cc$'], ['exclude', '^browser/geolocation/network_location_provider\\.(cc|h)$'], ['exclude', '^browser/geolocation/network_location_request\\.(cc|h)$'], + ['exclude', '^browser/tracing/trace_uploader\\.(cc|h)$'], ['exclude', '^browser/tracing/tracing_ui\\.(cc|h)$'], ['exclude', '^browser/speech/'], ['include', '^browser/speech/speech_recognition_dispatcher_host\\.(cc|h)$'], diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc index 50e77f5..f91f531 100644 --- a/content/public/common/content_switches.cc +++ b/content/public/common/content_switches.cc @@ -817,6 +817,8 @@ const char kTraceStartupDuration[] = "trace-startup-duration"; // all events since startup. const char kTraceStartupFile[] = "trace-startup-file"; +// Sets the target URL for uploading tracing data. +const char kTraceUploadURL[] = "trace-upload-url"; // Prioritizes the UI's command stream in the GPU process diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h index 4639bff..b2954ac 100644 --- a/content/public/common/content_switches.h +++ b/content/public/common/content_switches.h @@ -226,6 +226,7 @@ extern const char kTraceShutdownFile[]; extern const char kTraceStartup[]; extern const char kTraceStartupDuration[]; extern const char kTraceStartupFile[]; +extern const char kTraceUploadURL[]; CONTENT_EXPORT extern const char kUIPrioritizeInGpuProcess[]; CONTENT_EXPORT extern const char kUseDiscardableMemory[]; CONTENT_EXPORT extern const char kUseFakeUIForMediaStream[]; |