summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormmandlis@chromium.org <mmandlis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-27 20:52:06 +0000
committermmandlis@chromium.org <mmandlis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-27 20:52:06 +0000
commitb12387e66a2a39bed3faa6a9e4617272cf2ee2d2 (patch)
tree05afa38280b1e5795aa363065117765645f7b147
parent4ca013ad73bd2c6491527a4b5555c41843a34661 (diff)
downloadchromium_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/DEPS1
-rw-r--r--content/browser/tracing/trace_uploader.cc211
-rw-r--r--content/browser/tracing/trace_uploader.h91
-rw-r--r--content/browser/tracing/tracing_ui.cc115
-rw-r--r--content/browser/tracing/tracing_ui.h11
-rw-r--r--content/content_browser.gypi3
-rw-r--r--content/public/common/content_switches.cc2
-rw-r--r--content/public/common/content_switches.h1
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[];