// Copyright (c) 2012 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/tracing_ui.h" #include #include "base/base64.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/file_util.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_util.h" #include "base/values.h" #include "content/browser/tracing/tracing_controller_impl.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/url_constants.h" #include "grit/tracing_resources.h" namespace content { namespace { void OnGotCategories(const WebUIDataSource::GotDataCallback& callback, const std::set& categorySet) { scoped_ptr category_list(new base::ListValue()); for (std::set::const_iterator it = categorySet.begin(); it != categorySet.end(); it++) { category_list->AppendString(*it); } base::RefCountedString* res = new base::RefCountedString(); base::JSONWriter::Write(category_list.get(), &res->data()); callback.Run(res); } bool GetTracingOptions(const std::string& data64, std::string* category_filter_string, int* tracing_options) { std::string data; if (!base::Base64Decode(data64, &data)) { LOG(ERROR) << "Options were not base64 encoded."; return false; } scoped_ptr optionsRaw(base::JSONReader::Read(data)); if (!optionsRaw) { LOG(ERROR) << "Options were not valid JSON"; return false; } base::DictionaryValue* options; if (!optionsRaw->GetAsDictionary(&options)) { LOG(ERROR) << "Options must be dict"; return false; } bool use_system_tracing; bool use_continuous_tracing; bool use_sampling; bool options_ok = true; options_ok &= options->GetString("categoryFilter", category_filter_string); options_ok &= options->GetBoolean("useSystemTracing", &use_system_tracing); options_ok &= options->GetBoolean("useContinuousTracing", &use_continuous_tracing); options_ok &= options->GetBoolean("useSampling", &use_sampling); if (!options_ok) { LOG(ERROR) << "Malformed options"; return false; } *tracing_options = 0; if (use_system_tracing) *tracing_options |= TracingController::ENABLE_SYSTRACE; if (use_sampling) *tracing_options |= TracingController::ENABLE_SAMPLING; if (use_continuous_tracing) *tracing_options |= TracingController::RECORD_CONTINUOUSLY; return true; } void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback); bool BeginRecording(const std::string& data64, const WebUIDataSource::GotDataCallback& callback) { std::string category_filter_string; int tracing_options = 0; if (!GetTracingOptions(data64, &category_filter_string, &tracing_options)) return false; return TracingController::GetInstance()->EnableRecording( category_filter_string, static_cast(tracing_options), base::Bind(&OnRecordingEnabledAck, callback)); } void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback) { base::RefCountedString* res = new base::RefCountedString(); callback.Run(res); } void OnTraceBufferPercentFullResult( const WebUIDataSource::GotDataCallback& callback, float result) { std::string str = base::DoubleToString(result); callback.Run(base::RefCountedString::TakeString(&str)); } void ReadRecordingResult(const WebUIDataSource::GotDataCallback& callback, const base::FilePath& path) { std::string tmp; if (!base::ReadFileToString(path, &tmp)) LOG(ERROR) << "Failed to read file " << path.value(); base::DeleteFile(path, false); callback.Run(base::RefCountedString::TakeString(&tmp)); } void BeginReadingRecordingResult( const WebUIDataSource::GotDataCallback& callback, const base::FilePath& path) { BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, base::Bind(ReadRecordingResult, callback, path)); } void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback); bool EnableMonitoring(const std::string& data64, const WebUIDataSource::GotDataCallback& callback) { std::string category_filter_string; int tracing_options = 0; if (!GetTracingOptions(data64, &category_filter_string, &tracing_options)) return false; return TracingController::GetInstance()->EnableMonitoring( category_filter_string, static_cast(tracing_options), base::Bind(OnMonitoringEnabledAck, callback)); } void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback) { base::RefCountedString* res = new base::RefCountedString(); callback.Run(res); } void OnMonitoringDisabled(const WebUIDataSource::GotDataCallback& callback) { base::RefCountedString* res = new base::RefCountedString(); callback.Run(res); } void GetMonitoringStatus(const WebUIDataSource::GotDataCallback& callback) { bool is_monitoring; std::string category_filter; TracingController::Options options; TracingController::GetInstance()->GetMonitoringStatus( &is_monitoring, &category_filter, &options); scoped_ptr monitoring_options(new base::DictionaryValue()); monitoring_options->SetBoolean("isMonitoring", is_monitoring); monitoring_options->SetString("categoryFilter", category_filter); monitoring_options->SetBoolean("useSystemTracing", (options & TracingController::ENABLE_SYSTRACE) != 0); monitoring_options->SetBoolean("useContinuousTracing", (options & TracingController::RECORD_CONTINUOUSLY) != 0); monitoring_options->SetBoolean("useSampling", (options & TracingController::ENABLE_SAMPLING) != 0); std::string monitoring_options_json; base::JSONWriter::Write(monitoring_options.get(), &monitoring_options_json); base::RefCountedString* monitoring_options_base64 = new base::RefCountedString(); base::Base64Encode(monitoring_options_json, &monitoring_options_base64->data()); callback.Run(monitoring_options_base64); } void ReadMonitoringSnapshot(const WebUIDataSource::GotDataCallback& callback, const base::FilePath& path) { std::string tmp; if (!base::ReadFileToString(path, &tmp)) LOG(ERROR) << "Failed to read file " << path.value(); base::DeleteFile(path, false); callback.Run(base::RefCountedString::TakeString(&tmp)); } void OnMonitoringSnapshotCaptured( const WebUIDataSource::GotDataCallback& callback, const base::FilePath& path) { BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, base::Bind(ReadMonitoringSnapshot, callback, path)); } bool OnBeginJSONRequest(const std::string& path, const WebUIDataSource::GotDataCallback& callback) { if (path == "json/categories") { return TracingController::GetInstance()->GetCategories( base::Bind(OnGotCategories, callback)); } const char* beginRecordingPath = "json/begin_recording?"; if (StartsWithASCII(path, beginRecordingPath, true)) { std::string data = path.substr(strlen(beginRecordingPath)); return BeginRecording(data, callback); } if (path == "json/get_buffer_percent_full") { return TracingController::GetInstance()->GetTraceBufferPercentFull( base::Bind(OnTraceBufferPercentFullResult, callback)); } if (path == "json/end_recording") { return TracingController::GetInstance()->DisableRecording( base::FilePath(), base::Bind(BeginReadingRecordingResult, callback)); } const char* enableMonitoringPath = "json/begin_monitoring?"; if (path.find(enableMonitoringPath) == 0) { std::string data = path.substr(strlen(enableMonitoringPath)); return EnableMonitoring(data, callback); } if (path == "json/end_monitoring") { return TracingController::GetInstance()->DisableMonitoring( base::Bind(OnMonitoringDisabled, callback)); } if (path == "json/capture_monitoring") { TracingController::GetInstance()->CaptureMonitoringSnapshot( base::FilePath(), base::Bind(OnMonitoringSnapshotCaptured, callback)); return true; } if (path == "json/get_monitoring_status") { GetMonitoringStatus(callback); return true; } LOG(ERROR) << "Unhandled request to " << path; return false; } bool OnTracingRequest(const std::string& path, const WebUIDataSource::GotDataCallback& callback) { if (StartsWithASCII(path, "json/", true)) { if (!OnBeginJSONRequest(path, callback)) { std::string error("##ERROR##"); callback.Run(base::RefCountedString::TakeString(&error)); } return true; } return false; } } // namespace //////////////////////////////////////////////////////////////////////////////// // // TracingUI // //////////////////////////////////////////////////////////////////////////////// TracingUI::TracingUI(WebUI* web_ui) : WebUIController(web_ui) { // Set up the chrome://tracing/ source. BrowserContext* browser_context = web_ui->GetWebContents()->GetBrowserContext(); WebUIDataSource* source = WebUIDataSource::Create(kChromeUITracingHost); source->SetJsonPath("strings.js"); source->SetDefaultResource(IDR_TRACING_HTML); source->AddResourcePath("tracing.js", IDR_TRACING_JS); source->SetRequestFilter(base::Bind(OnTracingRequest)); WebUIDataSource::Add(browser_context, source); TracingControllerImpl::GetInstance()->RegisterTracingUI(this); } TracingUI::~TracingUI() { TracingControllerImpl::GetInstance()->UnregisterTracingUI(this); } void TracingUI::OnMonitoringStateChanged(bool is_monitoring) { web_ui()->CallJavascriptFunction( "onMonitoringStateChanged", base::FundamentalValue(is_monitoring)); } } // namespace content