Roll src/third_party/WebKit bf18a82:a9cee16 (svn 185297:185304)
[chromium-blink-merge.git] / chrome / test / chromedriver / performance_logger.cc
blob1da5b1c6a1af95c589b9fc002facb1a5c9ea555a
1 // Copyright 2014 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.
5 #include "chrome/test/chromedriver/performance_logger.h"
7 #include <string>
8 #include <vector>
10 #include "base/bind.h"
11 #include "base/json/json_writer.h"
12 #include "base/strings/string_util.h"
13 #include "base/values.h"
14 #include "chrome/test/chromedriver/chrome/browser_info.h"
15 #include "chrome/test/chromedriver/chrome/chrome.h"
16 #include "chrome/test/chromedriver/chrome/devtools_client.h"
17 #include "chrome/test/chromedriver/chrome/devtools_client_impl.h"
18 #include "chrome/test/chromedriver/chrome/log.h"
19 #include "chrome/test/chromedriver/chrome/status.h"
20 #include "chrome/test/chromedriver/session.h"
22 namespace {
24 // DevTools event domain prefixes to intercept.
25 const char* const kDomains[] = {"Network.", "Page.", "Timeline."};
27 // Whitelist of WebDriver commands on which to request buffered trace events.
28 const char* const kRequestTraceCommands[] = {"GetLog" /* required */,
29 "Navigate"};
31 bool IsBrowserwideClient(DevToolsClient* client) {
32 return (client->GetId() == DevToolsClientImpl::kBrowserwideDevToolsClientId);
35 bool IsEnabled(const PerfLoggingPrefs::InspectorDomainStatus& domain_status) {
36 return (domain_status ==
37 PerfLoggingPrefs::InspectorDomainStatus::kDefaultEnabled ||
38 domain_status ==
39 PerfLoggingPrefs::InspectorDomainStatus::kExplicitlyEnabled);
42 // Returns whether |command| is in kRequestTraceCommands[] (case-insensitive).
43 bool ShouldRequestTraceEvents(const std::string& command) {
44 for (size_t i_domain = 0; i_domain < arraysize(kRequestTraceCommands);
45 ++i_domain) {
46 if (base::strcasecmp(command.c_str(), kRequestTraceCommands[i_domain]) == 0)
47 return true;
49 return false;
52 // Returns whether the event belongs to one of kDomains.
53 bool ShouldLogEvent(const std::string& method) {
54 for (size_t i_domain = 0; i_domain < arraysize(kDomains); ++i_domain) {
55 if (StartsWithASCII(method, kDomains[i_domain], true /* case_sensitive */))
56 return true;
58 return false;
61 } // namespace
63 PerformanceLogger::PerformanceLogger(Log* log, const Session* session)
64 : log_(log),
65 session_(session),
66 browser_client_(NULL),
67 trace_buffering_(false) {}
69 PerformanceLogger::PerformanceLogger(Log* log,
70 const Session* session,
71 const PerfLoggingPrefs& prefs)
72 : log_(log),
73 session_(session),
74 prefs_(prefs),
75 browser_client_(NULL),
76 trace_buffering_(false) {}
78 bool PerformanceLogger::subscribes_to_browser() {
79 return true;
82 Status PerformanceLogger::OnConnected(DevToolsClient* client) {
83 if (IsBrowserwideClient(client)) {
84 browser_client_ = client;
85 if (!prefs_.trace_categories.empty()) {
86 Status status = StartTrace();
87 if (status.IsError())
88 return status;
90 return Status(kOk);
92 return EnableInspectorDomains(client);
95 Status PerformanceLogger::OnEvent(
96 DevToolsClient* client,
97 const std::string& method,
98 const base::DictionaryValue& params) {
99 if (IsBrowserwideClient(client)) {
100 return HandleTraceEvents(client, method, params);
101 } else {
102 return HandleInspectorEvents(client, method, params);
106 Status PerformanceLogger::BeforeCommand(const std::string& command_name) {
107 // Only dump trace buffer after tracing has been started.
108 if (trace_buffering_ && ShouldRequestTraceEvents(command_name)) {
109 Status status = CollectTraceEvents();
110 if (status.IsError())
111 return status;
113 return Status(kOk);
116 void PerformanceLogger::AddLogEntry(
117 Log::Level level,
118 const std::string& webview,
119 const std::string& method,
120 const base::DictionaryValue& params) {
121 base::DictionaryValue log_message_dict;
122 log_message_dict.SetString("webview", webview);
123 log_message_dict.SetString("message.method", method);
124 log_message_dict.Set("message.params", params.DeepCopy());
125 std::string log_message_json;
126 base::JSONWriter::Write(&log_message_dict, &log_message_json);
128 // TODO(klm): extract timestamp from params?
129 // Look at where it is for Page, Network, Timeline, and trace events.
130 log_->AddEntry(level, log_message_json);
133 void PerformanceLogger::AddLogEntry(
134 const std::string& webview,
135 const std::string& method,
136 const base::DictionaryValue& params) {
137 AddLogEntry(Log::kInfo, webview, method, params);
140 Status PerformanceLogger::EnableInspectorDomains(DevToolsClient* client) {
141 std::vector<std::string> enable_commands;
142 if (IsEnabled(prefs_.network))
143 enable_commands.push_back("Network.enable");
144 if (IsEnabled(prefs_.page))
145 enable_commands.push_back("Page.enable");
146 if (IsEnabled(prefs_.timeline)) {
147 // Timeline feed implicitly disabled when trace categories are specified.
148 // So even if kDefaultEnabled, don't enable unless empty |trace_categories|.
149 if (prefs_.trace_categories.empty() || prefs_.timeline ==
150 PerfLoggingPrefs::InspectorDomainStatus::kExplicitlyEnabled)
151 enable_commands.push_back("Timeline.start");
153 for (std::vector<std::string>::const_iterator it = enable_commands.begin();
154 it != enable_commands.end(); ++it) {
155 base::DictionaryValue params; // All the enable commands have empty params.
156 Status status = client->SendCommand(*it, params);
157 if (status.IsError())
158 return status;
160 return Status(kOk);
163 Status PerformanceLogger::HandleInspectorEvents(
164 DevToolsClient* client,
165 const std::string& method,
166 const base::DictionaryValue& params) {
167 if (!ShouldLogEvent(method))
168 return Status(kOk);
170 AddLogEntry(client->GetId(), method, params);
171 return Status(kOk);
174 Status PerformanceLogger::HandleTraceEvents(
175 DevToolsClient* client,
176 const std::string& method,
177 const base::DictionaryValue& params) {
178 if (method == "Tracing.tracingComplete") {
179 trace_buffering_ = false;
180 } else if (method == "Tracing.dataCollected") {
181 // The Tracing.dataCollected event contains a list of trace events.
182 // Add each one as an individual log entry of method Tracing.dataCollected.
183 const base::ListValue* traces;
184 if (!params.GetList("value", &traces)) {
185 return Status(kUnknownError,
186 "received DevTools trace data in unexpected format");
188 for (base::ListValue::const_iterator it = traces->begin();
189 it != traces->end();
190 ++it) {
191 base::DictionaryValue* event_dict;
192 if (!(*it)->GetAsDictionary(&event_dict))
193 return Status(kUnknownError, "trace event must be a dictionary");
194 AddLogEntry(client->GetId(), "Tracing.dataCollected", *event_dict);
196 } else if (method == "Tracing.bufferUsage") {
197 // 'value' will be between 0-1 and represents how full the DevTools trace
198 // buffer is. If the buffer is full, warn the user.
199 double buffer_usage = 0;
200 if (!params.GetDouble("value", &buffer_usage)) {
201 // Tracing.bufferUsage event will occur once per second, and it really
202 // only serves as a warning, so if we can't reliably tell whether the
203 // buffer is full, just fail silently instead of spamming the logs.
204 return Status(kOk);
206 if (buffer_usage >= 0.99999) {
207 base::DictionaryValue params;
208 std::string err("Chrome's trace buffer filled while collecting events, "
209 "so some trace events may have been lost");
210 params.SetString("error", err);
211 // Expose error to client via perf log using same format as other entries.
212 AddLogEntry(Log::kWarning,
213 DevToolsClientImpl::kBrowserwideDevToolsClientId,
214 "Tracing.bufferUsage", params);
215 LOG(WARNING) << err;
218 return Status(kOk);
221 bool PerformanceLogger::ShouldReportTracingError() {
222 // Chromium builds 1967-2000, which correspond to Blink revisions 172887-
223 // 174227, contain a regression where Tracing.start and Tracing.end commands
224 // erroneously return error -32601 "no such method". The commands still work.
225 if (session_->chrome) {
226 const BrowserInfo* browser_info = session_->chrome->GetBrowserInfo();
228 bool should_report_error = true;
229 if (browser_info->browser_name == "chrome") {
230 should_report_error = !(browser_info->build_no >= 1967 &&
231 browser_info->build_no <= 2000);
232 } else {
233 should_report_error = !(browser_info->blink_revision >= 172887 &&
234 browser_info->blink_revision <= 174227);
236 return should_report_error;
239 // We're not yet able to tell the Chrome version, so don't report this error.
240 return false;
243 Status PerformanceLogger::StartTrace() {
244 if (!browser_client_) {
245 return Status(kUnknownError, "tried to start tracing, but connection to "
246 "browser was not yet established");
248 if (trace_buffering_) {
249 LOG(WARNING) << "tried to start tracing, but a trace was already started";
250 return Status(kOk);
252 base::DictionaryValue params;
253 params.SetString("categories", prefs_.trace_categories);
254 // Ask DevTools to report buffer usage.
255 params.SetInteger("bufferUsageReportingInterval",
256 prefs_.buffer_usage_reporting_interval);
257 Status status = browser_client_->SendCommand("Tracing.start", params);
258 if (status.IsError() && ShouldReportTracingError()) {
259 LOG(ERROR) << "error when starting trace: " << status.message();
260 return status;
262 trace_buffering_ = true;
263 return Status(kOk);
266 Status PerformanceLogger::CollectTraceEvents() {
267 if (!browser_client_) {
268 return Status(kUnknownError, "tried to collect trace events, but "
269 "connection to browser was not yet established");
271 if (!trace_buffering_) {
272 return Status(kUnknownError, "tried to collect trace events, but tracing "
273 "was not started");
276 Status status = browser_client_->SendCommand("Tracing.end",
277 base::DictionaryValue());
278 if (status.IsError() && ShouldReportTracingError()) {
279 LOG(ERROR) << "error when stopping trace: " << status.message();
280 return status;
283 // Block up to 30 seconds until Tracing.tracingComplete event is received.
284 status = browser_client_->HandleEventsUntil(
285 base::Bind(&PerformanceLogger::IsTraceDone, base::Unretained(this)),
286 base::TimeDelta::FromSeconds(30));
287 if (status.IsError())
288 return status;
290 return StartTrace();
293 Status PerformanceLogger::IsTraceDone(bool* trace_done) const {
294 *trace_done = !trace_buffering_;
295 return Status(kOk);