1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/browser/tracing/tracing_ui.h" 6 7#include <string> 8 9#include "base/base64.h" 10#include "base/bind.h" 11#include "base/bind_helpers.h" 12#include "base/file_util.h" 13#include "base/json/json_reader.h" 14#include "base/json/json_writer.h" 15#include "base/memory/scoped_ptr.h" 16#include "base/strings/string_number_conversions.h" 17#include "base/strings/string_util.h" 18#include "base/values.h" 19#include "content/browser/tracing/grit/tracing_resources.h" 20#include "content/browser/tracing/tracing_controller_impl.h" 21#include "content/public/browser/browser_thread.h" 22#include "content/public/browser/tracing_controller.h" 23#include "content/public/browser/web_contents.h" 24#include "content/public/browser/web_ui.h" 25#include "content/public/browser/web_ui_data_source.h" 26#include "content/public/common/url_constants.h" 27 28namespace content { 29namespace { 30 31void OnGotCategories(const WebUIDataSource::GotDataCallback& callback, 32 const std::set<std::string>& categorySet) { 33 34 scoped_ptr<base::ListValue> category_list(new base::ListValue()); 35 for (std::set<std::string>::const_iterator it = categorySet.begin(); 36 it != categorySet.end(); it++) { 37 category_list->AppendString(*it); 38 } 39 40 base::RefCountedString* res = new base::RefCountedString(); 41 base::JSONWriter::Write(category_list.get(), &res->data()); 42 callback.Run(res); 43} 44 45bool GetTracingOptions(const std::string& data64, 46 std::string* category_filter_string, 47 int* tracing_options) { 48 std::string data; 49 if (!base::Base64Decode(data64, &data)) { 50 LOG(ERROR) << "Options were not base64 encoded."; 51 return false; 52 } 53 54 scoped_ptr<base::Value> optionsRaw(base::JSONReader::Read(data)); 55 if (!optionsRaw) { 56 LOG(ERROR) << "Options were not valid JSON"; 57 return false; 58 } 59 base::DictionaryValue* options; 60 if (!optionsRaw->GetAsDictionary(&options)) { 61 LOG(ERROR) << "Options must be dict"; 62 return false; 63 } 64 65 bool use_system_tracing; 66 bool use_continuous_tracing; 67 bool use_sampling; 68 69 bool options_ok = true; 70 options_ok &= options->GetString("categoryFilter", category_filter_string); 71 options_ok &= options->GetBoolean("useSystemTracing", &use_system_tracing); 72 options_ok &= options->GetBoolean("useContinuousTracing", 73 &use_continuous_tracing); 74 options_ok &= options->GetBoolean("useSampling", &use_sampling); 75 if (!options_ok) { 76 LOG(ERROR) << "Malformed options"; 77 return false; 78 } 79 80 *tracing_options = 0; 81 if (use_system_tracing) 82 *tracing_options |= TracingController::ENABLE_SYSTRACE; 83 if (use_sampling) 84 *tracing_options |= TracingController::ENABLE_SAMPLING; 85 if (use_continuous_tracing) 86 *tracing_options |= TracingController::RECORD_CONTINUOUSLY; 87 return true; 88} 89 90void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback); 91 92bool BeginRecording(const std::string& data64, 93 const WebUIDataSource::GotDataCallback& callback) { 94 std::string category_filter_string; 95 int tracing_options = 0; 96 if (!GetTracingOptions(data64, &category_filter_string, &tracing_options)) 97 return false; 98 99 return TracingController::GetInstance()->EnableRecording( 100 category_filter_string, 101 static_cast<TracingController::Options>(tracing_options), 102 base::Bind(&OnRecordingEnabledAck, callback)); 103} 104 105void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback) { 106 base::RefCountedString* res = new base::RefCountedString(); 107 callback.Run(res); 108} 109 110void OnTraceBufferPercentFullResult( 111 const WebUIDataSource::GotDataCallback& callback, float result) { 112 std::string str = base::DoubleToString(result); 113 callback.Run(base::RefCountedString::TakeString(&str)); 114} 115 116void ReadRecordingResult(const WebUIDataSource::GotDataCallback& callback, 117 const base::FilePath& path) { 118 std::string tmp; 119 if (!base::ReadFileToString(path, &tmp)) 120 LOG(ERROR) << "Failed to read file " << path.value(); 121 base::DeleteFile(path, false); 122 callback.Run(base::RefCountedString::TakeString(&tmp)); 123} 124 125void BeginReadingRecordingResult( 126 const WebUIDataSource::GotDataCallback& callback, 127 const base::FilePath& path) { 128 BrowserThread::PostTask( 129 BrowserThread::FILE, FROM_HERE, 130 base::Bind(ReadRecordingResult, callback, path)); 131} 132 133void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback); 134 135bool EnableMonitoring(const std::string& data64, 136 const WebUIDataSource::GotDataCallback& callback) { 137 std::string category_filter_string; 138 int tracing_options = 0; 139 if (!GetTracingOptions(data64, &category_filter_string, &tracing_options)) 140 return false; 141 142 return TracingController::GetInstance()->EnableMonitoring( 143 category_filter_string, 144 static_cast<TracingController::Options>(tracing_options), 145 base::Bind(OnMonitoringEnabledAck, callback)); 146} 147 148void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback) { 149 base::RefCountedString* res = new base::RefCountedString(); 150 callback.Run(res); 151} 152 153void OnMonitoringDisabled(const WebUIDataSource::GotDataCallback& callback) { 154 base::RefCountedString* res = new base::RefCountedString(); 155 callback.Run(res); 156} 157 158void GetMonitoringStatus(const WebUIDataSource::GotDataCallback& callback) { 159 bool is_monitoring; 160 std::string category_filter; 161 TracingController::Options options; 162 TracingController::GetInstance()->GetMonitoringStatus( 163 &is_monitoring, &category_filter, &options); 164 165 scoped_ptr<base::DictionaryValue> 166 monitoring_options(new base::DictionaryValue()); 167 monitoring_options->SetBoolean("isMonitoring", is_monitoring); 168 monitoring_options->SetString("categoryFilter", category_filter); 169 monitoring_options->SetBoolean("useSystemTracing", 170 (options & TracingController::ENABLE_SYSTRACE) != 0); 171 monitoring_options->SetBoolean("useContinuousTracing", 172 (options & TracingController::RECORD_CONTINUOUSLY) != 0); 173 monitoring_options->SetBoolean("useSampling", 174 (options & TracingController::ENABLE_SAMPLING) != 0); 175 176 std::string monitoring_options_json; 177 base::JSONWriter::Write(monitoring_options.get(), &monitoring_options_json); 178 179 base::RefCountedString* monitoring_options_base64 = 180 new base::RefCountedString(); 181 base::Base64Encode(monitoring_options_json, 182 &monitoring_options_base64->data()); 183 callback.Run(monitoring_options_base64); 184} 185 186void ReadMonitoringSnapshot(const WebUIDataSource::GotDataCallback& callback, 187 const base::FilePath& path) { 188 std::string tmp; 189 if (!base::ReadFileToString(path, &tmp)) 190 LOG(ERROR) << "Failed to read file " << path.value(); 191 base::DeleteFile(path, false); 192 callback.Run(base::RefCountedString::TakeString(&tmp)); 193} 194 195void OnMonitoringSnapshotCaptured( 196 const WebUIDataSource::GotDataCallback& callback, 197 const base::FilePath& path) { 198 BrowserThread::PostTask( 199 BrowserThread::FILE, FROM_HERE, 200 base::Bind(ReadMonitoringSnapshot, callback, path)); 201} 202 203bool OnBeginJSONRequest(const std::string& path, 204 const WebUIDataSource::GotDataCallback& callback) { 205 if (path == "json/categories") { 206 return TracingController::GetInstance()->GetCategories( 207 base::Bind(OnGotCategories, callback)); 208 } 209 210 const char* beginRecordingPath = "json/begin_recording?"; 211 if (StartsWithASCII(path, beginRecordingPath, true)) { 212 std::string data = path.substr(strlen(beginRecordingPath)); 213 return BeginRecording(data, callback); 214 } 215 if (path == "json/get_buffer_percent_full") { 216 return TracingController::GetInstance()->GetTraceBufferPercentFull( 217 base::Bind(OnTraceBufferPercentFullResult, callback)); 218 } 219 if (path == "json/end_recording") { 220 return TracingController::GetInstance()->DisableRecording( 221 base::FilePath(), base::Bind(BeginReadingRecordingResult, callback)); 222 } 223 224 const char* enableMonitoringPath = "json/begin_monitoring?"; 225 if (path.find(enableMonitoringPath) == 0) { 226 std::string data = path.substr(strlen(enableMonitoringPath)); 227 return EnableMonitoring(data, callback); 228 } 229 if (path == "json/end_monitoring") { 230 return TracingController::GetInstance()->DisableMonitoring( 231 base::Bind(OnMonitoringDisabled, callback)); 232 } 233 if (path == "json/capture_monitoring") { 234 TracingController::GetInstance()->CaptureMonitoringSnapshot( 235 base::FilePath(), base::Bind(OnMonitoringSnapshotCaptured, callback)); 236 return true; 237 } 238 if (path == "json/get_monitoring_status") { 239 GetMonitoringStatus(callback); 240 return true; 241 } 242 243 LOG(ERROR) << "Unhandled request to " << path; 244 return false; 245} 246 247bool OnTracingRequest(const std::string& path, 248 const WebUIDataSource::GotDataCallback& callback) { 249 if (StartsWithASCII(path, "json/", true)) { 250 if (!OnBeginJSONRequest(path, callback)) { 251 std::string error("##ERROR##"); 252 callback.Run(base::RefCountedString::TakeString(&error)); 253 } 254 return true; 255 } 256 return false; 257} 258 259} // namespace 260 261 262//////////////////////////////////////////////////////////////////////////////// 263// 264// TracingUI 265// 266//////////////////////////////////////////////////////////////////////////////// 267 268TracingUI::TracingUI(WebUI* web_ui) : WebUIController(web_ui) { 269 // Set up the chrome://tracing/ source. 270 BrowserContext* browser_context = 271 web_ui->GetWebContents()->GetBrowserContext(); 272 273 WebUIDataSource* source = WebUIDataSource::Create(kChromeUITracingHost); 274 source->SetJsonPath("strings.js"); 275 source->SetDefaultResource(IDR_TRACING_HTML); 276 source->AddResourcePath("tracing.js", IDR_TRACING_JS); 277 source->SetRequestFilter(base::Bind(OnTracingRequest)); 278 WebUIDataSource::Add(browser_context, source); 279 TracingControllerImpl::GetInstance()->RegisterTracingUI(this); 280} 281 282TracingUI::~TracingUI() { 283 TracingControllerImpl::GetInstance()->UnregisterTracingUI(this); 284} 285 286void TracingUI::OnMonitoringStateChanged(bool is_monitoring) { 287 web_ui()->CallJavascriptFunction( 288 "onMonitoringStateChanged", base::FundamentalValue(is_monitoring)); 289} 290 291} // namespace content 292