Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(320)

Side by Side Diff: chrome/test/chromedriver/performance_logger.cc

Issue 454133003: [ChromeDriver] Implementing tracing support (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixing import ordering Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/test/chromedriver/performance_logger.h"
6
5 #include <string> 7 #include <string>
6 #include <vector> 8 #include <vector>
7 9
8 #include "chrome/test/chromedriver/performance_logger.h" 10 #include "base/bind.h"
9
10 #include "base/json/json_writer.h" 11 #include "base/json/json_writer.h"
11 #include "base/strings/string_util.h" 12 #include "base/strings/string_util.h"
12 #include "base/values.h" 13 #include "base/values.h"
14 #include "chrome/test/chromedriver/chrome/chrome.h"
13 #include "chrome/test/chromedriver/chrome/devtools_client.h" 15 #include "chrome/test/chromedriver/chrome/devtools_client.h"
14 #include "chrome/test/chromedriver/chrome/devtools_client_impl.h" 16 #include "chrome/test/chromedriver/chrome/devtools_client_impl.h"
15 #include "chrome/test/chromedriver/chrome/log.h" 17 #include "chrome/test/chromedriver/chrome/log.h"
16 #include "chrome/test/chromedriver/chrome/status.h" 18 #include "chrome/test/chromedriver/chrome/status.h"
19 #include "chrome/test/chromedriver/chrome/version.h"
20 #include "chrome/test/chromedriver/session.h"
17 21
18 namespace { 22 namespace {
19 23
20 // DevTools event domain prefixes to intercept. 24 // DevTools event domain prefixes to intercept.
21 const char* const kDomains[] = {"Network.", "Page.", "Timeline."}; 25 const char* const kDomains[] = {"Network.", "Page.", "Timeline."};
22 26
27 // Whitelist of WebDriver commands on which to request buffered trace events.
28 const char* const kRequestTraceCommands[] = {"GetLog" /* required */,
stgao 2014/08/10 02:37:03 Just an idea: do we want this to be configurable i
johnmoore 2014/08/11 16:00:01 I don't think we should expose something that comp
stgao 2014/08/15 17:43:22 Not sure if we want to do every command, because s
29 "Navigate"};
30
31 bool IsBrowserwideClient(DevToolsClient* client) {
32 return (client->GetId() == DevToolsClientImpl::kBrowserwideDevToolsClientId);
33 }
34
35 bool IsEnabled(const PerfLoggingPrefs::InspectorDomainStatus& domain_status) {
36 return (domain_status ==
37 PerfLoggingPrefs::InspectorDomainStatus::kDefaultEnabled ||
38 domain_status ==
39 PerfLoggingPrefs::InspectorDomainStatus::kExplicitlyEnabled);
40 }
41
42 // Returns whether method is in kRequestTraceCommands[] (case-insensitive).
43 bool ShouldRequestTraceEvents(const std::string& method) {
stgao 2014/08/10 02:37:03 |method| -> |command|?
johnmoore 2014/08/11 16:00:01 Done.
44 for (size_t i_domain = 0; i_domain < arraysize(kRequestTraceCommands);
45 ++i_domain) {
46 if (base::strcasecmp(method.c_str(), kRequestTraceCommands[i_domain]) == 0)
47 return true;
48 }
49 return false;
50 }
51
23 // Returns whether the event belongs to one of kDomains. 52 // Returns whether the event belongs to one of kDomains.
24 bool ShouldLogEvent(const std::string& method) { 53 bool ShouldLogEvent(const std::string& method) {
25 for (size_t i_domain = 0; i_domain < arraysize(kDomains); ++i_domain) { 54 for (size_t i_domain = 0; i_domain < arraysize(kDomains); ++i_domain) {
26 if (StartsWithASCII(method, kDomains[i_domain], true /* case_sensitive */)) 55 if (StartsWithASCII(method, kDomains[i_domain], true /* case_sensitive */))
27 return true; 56 return true;
28 } 57 }
29 return false; 58 return false;
30 } 59 }
31 60
32 bool IsEnabled(const PerfLoggingPrefs::InspectorDomainStatus& domain_status) {
33 return (domain_status ==
34 PerfLoggingPrefs::InspectorDomainStatus::kDefaultEnabled ||
35 domain_status ==
36 PerfLoggingPrefs::InspectorDomainStatus::kExplicitlyEnabled);
37 }
38
39 } // namespace 61 } // namespace
40 62
41 PerformanceLogger::PerformanceLogger(Log* log) 63 PerformanceLogger::PerformanceLogger(Log* log, const Session* session)
42 : log_(log) {} 64 : log_(log),
65 session_(session),
66 browser_client_(NULL),
67 trace_buffering_(false) {}
43 68
44 PerformanceLogger::PerformanceLogger(Log* log, const PerfLoggingPrefs& prefs) 69 PerformanceLogger::PerformanceLogger(Log* log,
70 const Session* session,
71 const PerfLoggingPrefs& prefs)
45 : log_(log), 72 : log_(log),
46 prefs_(prefs) { 73 session_(session),
47 if (!prefs_.trace_categories.empty()) { 74 prefs_(prefs),
48 LOG(WARNING) << "Ignoring trace categories because tracing support not yet " 75 browser_client_(NULL),
49 << "implemented: " << prefs_.trace_categories; 76 trace_buffering_(false) {}
50 prefs_.trace_categories = "";
51 }
52 }
53 77
54 bool PerformanceLogger::subscribes_to_browser() { 78 bool PerformanceLogger::subscribes_to_browser() {
55 return true; 79 return true;
56 } 80 }
57 81
58 Status PerformanceLogger::OnConnected(DevToolsClient* client) { 82 Status PerformanceLogger::OnConnected(DevToolsClient* client) {
59 if (client->GetId() == DevToolsClientImpl::kBrowserwideDevToolsClientId) { 83 if (IsBrowserwideClient(client)) {
60 // TODO(johnmoore): Implement tracing log. 84 browser_client_ = client;
85 if (!prefs_.trace_categories.empty()) {
86 Status status = StartTrace();
87 if (status.IsError())
88 return status;
89 }
61 return Status(kOk); 90 return Status(kOk);
62 } 91 }
92 return EnableInspectorDomains(client);
93 }
94
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);
103 }
104 }
105
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;
112 }
113 return Status(kOk);
114 }
115
116 void PerformanceLogger::AddLogEntry(
117 const std::string& webview,
118 const std::string& method,
119 const base::DictionaryValue& params) {
120 base::DictionaryValue log_message_dict;
121 log_message_dict.SetString("webview", webview);
122 log_message_dict.SetString("message.method", method);
123 log_message_dict.Set("message.params", params.DeepCopy());
124 std::string log_message_json;
125 base::JSONWriter::Write(&log_message_dict, &log_message_json);
126
127 // TODO(klm): extract timestamp from params?
128 // Look at where it is for Page, Network, Timeline, and trace events.
129 log_->AddEntry(Log::kInfo, log_message_json);
130 }
131
132 Status PerformanceLogger::EnableInspectorDomains(DevToolsClient* client) {
63 std::vector<std::string> enable_commands; 133 std::vector<std::string> enable_commands;
64 if (IsEnabled(prefs_.network)) 134 if (IsEnabled(prefs_.network))
65 enable_commands.push_back("Network.enable"); 135 enable_commands.push_back("Network.enable");
66 if (IsEnabled(prefs_.page)) 136 if (IsEnabled(prefs_.page))
67 enable_commands.push_back("Page.enable"); 137 enable_commands.push_back("Page.enable");
68 if (IsEnabled(prefs_.timeline)) { 138 if (IsEnabled(prefs_.timeline)) {
69 // Timeline feed implicitly disabled when trace categories are specified. 139 // Timeline feed implicitly disabled when trace categories are specified.
70 // So even if kDefaultEnabled, don't enable unless empty |trace_categories|. 140 // So even if kDefaultEnabled, don't enable unless empty |trace_categories|.
71 if (prefs_.trace_categories.empty() || prefs_.timeline == 141 if (prefs_.trace_categories.empty() || prefs_.timeline ==
72 PerfLoggingPrefs::InspectorDomainStatus::kExplicitlyEnabled) 142 PerfLoggingPrefs::InspectorDomainStatus::kExplicitlyEnabled)
73 enable_commands.push_back("Timeline.start"); 143 enable_commands.push_back("Timeline.start");
74 } 144 }
75 for (std::vector<std::string>::const_iterator it = enable_commands.begin(); 145 for (std::vector<std::string>::const_iterator it = enable_commands.begin();
76 it != enable_commands.end(); ++it) { 146 it != enable_commands.end(); ++it) {
77 base::DictionaryValue params; // All the enable commands have empty params. 147 base::DictionaryValue params; // All the enable commands have empty params.
78 Status status = client->SendCommand(*it, params); 148 Status status = client->SendCommand(*it, params);
79 if (status.IsError()) 149 if (status.IsError())
80 return status; 150 return status;
81 } 151 }
82 return Status(kOk); 152 return Status(kOk);
83 } 153 }
84 154
85 Status PerformanceLogger::OnEvent( 155 Status PerformanceLogger::HandleInspectorEvents(
86 DevToolsClient* client, 156 DevToolsClient* client,
87 const std::string& method, 157 const std::string& method,
88 const base::DictionaryValue& params) { 158 const base::DictionaryValue& params) {
159
stgao 2014/08/10 02:37:04 This line could be removed.
johnmoore 2014/08/11 16:00:02 Done.
89 if (!ShouldLogEvent(method)) 160 if (!ShouldLogEvent(method))
90 return Status(kOk); 161 return Status(kOk);
91 162
92 base::DictionaryValue log_message_dict; 163 AddLogEntry(client->GetId(), method, params);
93 log_message_dict.SetString("webview", client->GetId());
94 log_message_dict.SetString("message.method", method);
95 log_message_dict.Set("message.params", params.DeepCopy());
96 std::string log_message_json;
97 // TODO(klm): extract timestamp from params?
98 // Look at where it is for Page, Network, Timeline events.
99 base::JSONWriter::Write(&log_message_dict, &log_message_json);
100
101 log_->AddEntry(Log::kInfo, log_message_json);
102 return Status(kOk); 164 return Status(kOk);
103 } 165 }
104 166
105 // TODO(johnmoore): Use BeforeCommand to implement tracing log. 167 Status PerformanceLogger::HandleTraceEvents(
106 Status PerformanceLogger::BeforeCommand(const std::string& command_name) { 168 DevToolsClient* client,
169 const std::string& method,
170 const base::DictionaryValue& params) {
171 if (method == "Tracing.tracingComplete") {
172 trace_buffering_ = false;
173 } else if (method == "Tracing.dataCollected") {
174 // The Tracing.dataCollected event contains a list of trace events.
175 // Add each one as an individual log entry of method Tracing.dataCollected.
176 const base::ListValue* traces;
177 if (!params.GetList("value", &traces)) {
178 return Status(kUnknownError,
179 "Received DevTools trace data in unexpected format");
stgao 2014/08/10 02:37:03 error message usually starts with lowercase unless
johnmoore 2014/08/11 16:00:01 Done.
180 }
181 for (base::ListValue::const_iterator it = traces->begin();
182 it != traces->end();
183 ++it) {
184 base::DictionaryValue* event_dict;
185 if (!(*it)->GetAsDictionary(&event_dict))
186 return Status(kUnknownError, "trace event must be a dictionary");
187 AddLogEntry(client->GetId(), "Tracing.dataCollected", *event_dict);
188 }
189 } else if (method == "Tracing.bufferUsage") {
190 // 'value' will be between 0-1 and represents how full the DevTools trace
191 // buffer is. If the buffer is full, warn the user.
192 double buffer_usage = 0;
193 if (!params.GetDouble("value", &buffer_usage)) {
194 // Tracing.bufferUsage event will occur once per second, and it really
195 // only serves as a warning, so if we can't reliably tell whether the
196 // buffer is full, just fail silently instead of spamming the logs.
197 return Status(kOk);
198 }
199 if (buffer_usage >= 1.0) {
stgao 2014/08/10 02:37:04 Floating point comparison is not accurate. Maybe u
johnmoore 2014/08/11 16:00:01 Done.
200 LOG(WARNING) << "Chrome's trace buffer filled while collecting events, "
stgao 2014/08/10 02:37:04 This warning is on server side. Do we want to mak
johnmoore 2014/08/11 16:00:01 Good idea. I've exposed the message to the user by
201 << "so some trace events may have been lost";
202 }
203 }
107 return Status(kOk); 204 return Status(kOk);
108 } 205 }
206
207 bool PerformanceLogger::ShouldReportTracingError() {
208 // Chromium builds 1967-2000, which correspond to Blink revisions 172887-
209 // 174227, contain a regression where Tracing.start and Tracing.end commands
210 // erroneously return error -32601 "no such method". The commands still work.
211 if (session_->chrome) {
212 const BrowserInfo* browser_info = session_->chrome->GetBrowserInfo();
213
214 bool should_report_error = true;
215 if (browser_info->browser_name == "chrome") {
216 should_report_error = !(browser_info->build_no >= 1967 &&
217 browser_info->build_no <= 2000);
218 } else {
219 should_report_error = !(browser_info->blink_revision >= 172887 &&
220 browser_info->blink_revision <= 174227);
221 }
222 return should_report_error;
223 }
224 // We're not yet able to tell the Chrome version, so don't report this error.
stgao 2014/08/10 02:37:04 Maybe add an empty line before this one.
johnmoore 2014/08/11 16:00:01 Done.
225 return false;
226 }
227
228 Status PerformanceLogger::StartTrace() {
229 CHECK(browser_client_);
230 if (trace_buffering_) {
231 LOG(WARNING) << "Ignoring PerformanceLogger::StartTrace because tracing "
232 << "was already started";
233 return Status(kOk);
234 }
235 base::DictionaryValue params;
236 params.SetString("categories", prefs_.trace_categories);
237 // Ask DevTools to report buffer usage percentage once per second.
238 params.SetInteger("bufferUsageReportingInterval", 1000 /* milliseconds */);
stgao 2014/08/10 02:37:03 Will this be too frequent?
johnmoore 2014/08/11 16:00:02 This is the default value that I've seen used in C
stgao 2014/08/15 17:43:22 Vote for making it configurable in perf log settin
239 Status status = browser_client_->SendCommand("Tracing.start", params);
240 if (status.IsError() && ShouldReportTracingError()) {
241 LOG(ERROR) << "Error when starting trace: " << status.message();
242 return status;
243 }
244 trace_buffering_ = true;
245 return Status(kOk);
246 }
247
248 Status PerformanceLogger::CollectTraceEvents() {
249 CHECK(browser_client_);
250 if (!trace_buffering_) {
251 return Status(kUnknownError, "PerformanceLogger::CollectTraceEvents called,"
stgao 2014/08/10 02:37:03 Maybe remove PerformanceLogger::CollectTraceEvents
johnmoore 2014/08/11 16:00:01 Done.
252 " but tracing was not started");
253 }
254 Status status = browser_client_->SendCommand("Tracing.end",
255 base::DictionaryValue());
256 if (status.IsError() && ShouldReportTracingError()) {
257 LOG(ERROR) << "Error when stopping trace: " << status.message();
258 return status;
259 }
260 // Block up to 30 seconds until Tracing.tracingComplete event is received.
stgao 2014/08/10 02:37:03 Add an empty line for each logic block. That will
johnmoore 2014/08/11 16:00:01 Done.
261 status = browser_client_->HandleEventsUntil(
262 base::Bind(&PerformanceLogger::IsTraceDone, base::Unretained(this)),
263 base::TimeDelta::FromSeconds(30));
264 if (status.IsError())
265 return status;
266 return StartTrace();
267 }
268
269 Status PerformanceLogger::IsTraceDone(bool* trace_done) const {
270 *trace_done = !trace_buffering_;
271 return Status(kOk);
272 }
OLDNEW
« no previous file with comments | « chrome/test/chromedriver/performance_logger.h ('k') | chrome/test/chromedriver/performance_logger_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698