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
28 namespace content {
29 namespace {
30
OnGotCategories(const WebUIDataSource::GotDataCallback & callback,const std::set<std::string> & categorySet)31 void 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
GetTracingOptions(const std::string & data64,std::string * category_filter_string,int * tracing_options)45 bool 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
90 void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback);
91
BeginRecording(const std::string & data64,const WebUIDataSource::GotDataCallback & callback)92 bool 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
OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback & callback)105 void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback) {
106 base::RefCountedString* res = new base::RefCountedString();
107 callback.Run(res);
108 }
109
OnTraceBufferPercentFullResult(const WebUIDataSource::GotDataCallback & callback,float result)110 void OnTraceBufferPercentFullResult(
111 const WebUIDataSource::GotDataCallback& callback, float result) {
112 std::string str = base::DoubleToString(result);
113 callback.Run(base::RefCountedString::TakeString(&str));
114 }
115
ReadRecordingResult(const WebUIDataSource::GotDataCallback & callback,const base::FilePath & path)116 void 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
BeginReadingRecordingResult(const WebUIDataSource::GotDataCallback & callback,const base::FilePath & path)125 void 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
133 void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback);
134
EnableMonitoring(const std::string & data64,const WebUIDataSource::GotDataCallback & callback)135 bool 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
OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback & callback)148 void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback) {
149 base::RefCountedString* res = new base::RefCountedString();
150 callback.Run(res);
151 }
152
OnMonitoringDisabled(const WebUIDataSource::GotDataCallback & callback)153 void OnMonitoringDisabled(const WebUIDataSource::GotDataCallback& callback) {
154 base::RefCountedString* res = new base::RefCountedString();
155 callback.Run(res);
156 }
157
GetMonitoringStatus(const WebUIDataSource::GotDataCallback & callback)158 void 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
ReadMonitoringSnapshot(const WebUIDataSource::GotDataCallback & callback,const base::FilePath & path)186 void 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
OnMonitoringSnapshotCaptured(const WebUIDataSource::GotDataCallback & callback,const base::FilePath & path)195 void 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
OnBeginJSONRequest(const std::string & path,const WebUIDataSource::GotDataCallback & callback)203 bool 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
OnTracingRequest(const std::string & path,const WebUIDataSource::GotDataCallback & callback)247 bool 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
TracingUI(WebUI * web_ui)268 TracingUI::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
~TracingUI()282 TracingUI::~TracingUI() {
283 TracingControllerImpl::GetInstance()->UnregisterTracingUI(this);
284 }
285
OnMonitoringStateChanged(bool is_monitoring)286 void TracingUI::OnMonitoringStateChanged(bool is_monitoring) {
287 web_ui()->CallJavascriptFunction(
288 "onMonitoringStateChanged", base::FundamentalValue(is_monitoring));
289 }
290
291 } // namespace content
292