OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include <algorithm> | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/debug/profiler.h" | |
9 #include "base/memory/weak_ptr.h" | |
10 #include "base/strings/string_number_conversions.h" | |
11 #include "base/strings/stringprintf.h" | |
12 #include "mojo/application/application_runner_chromium.h" | |
13 #include "mojo/public/c/system/main.h" | |
14 #include "mojo/public/cpp/application/application_delegate.h" | |
15 #include "mojo/public/cpp/application/application_impl.h" | |
16 #include "net/base/net_errors.h" | |
17 #include "net/server/http_server.h" | |
18 #include "net/server/http_server_request_info.h" | |
19 #include "net/socket/tcp_server_socket.h" | |
20 #include "services/tracing/tracing.mojom.h" | |
21 #include "sky/tools/debugger/debugger.mojom.h" | |
22 #include "sky/tools/debugger/prompt/trace_collector.h" | |
23 | |
24 namespace sky { | |
25 namespace debugger { | |
26 namespace { | |
27 | |
28 const size_t kMinSendBufferSize = 1024 * 1024; | |
29 } | |
30 | |
31 class Prompt : public mojo::ApplicationDelegate, | |
32 public net::HttpServer::Delegate { | |
33 public: | |
34 Prompt() | |
35 : is_tracing_(false), | |
36 weak_ptr_factory_(this) { | |
37 } | |
38 virtual ~Prompt() { | |
39 } | |
40 | |
41 private: | |
42 // Overridden from mojo::ApplicationDelegate: | |
43 virtual void Initialize(mojo::ApplicationImpl* app) override { | |
44 app->ConnectToService("mojo:tracing", &tracing_); | |
45 // Format: --args-for="app_url command_port" | |
46 if (app->args().size() < 2) { | |
47 LOG(ERROR) << "--args-for required to specify command_port"; | |
48 mojo::ApplicationImpl::Terminate(); | |
49 return; | |
50 } | |
51 | |
52 base::StringToUint(app->args()[1], &command_port_); | |
53 | |
54 scoped_ptr<net::ServerSocket> server_socket( | |
55 new net::TCPServerSocket(NULL, net::NetLog::Source())); | |
56 int result = server_socket->ListenWithAddressAndPort("0.0.0.0", command_port
_, 1); | |
57 if (result != net::OK) { | |
58 LOG(ERROR) << "Failed to bind to port " << command_port_ | |
59 << " skydb commands will not work."; | |
60 mojo::ApplicationImpl::Terminate(); | |
61 return; | |
62 } | |
63 web_server_.reset(new net::HttpServer(server_socket.Pass(), this)); | |
64 } | |
65 | |
66 virtual bool ConfigureIncomingConnection( | |
67 mojo::ApplicationConnection* connection) override { | |
68 connection->ConnectToService(&debugger_); | |
69 return true; | |
70 } | |
71 | |
72 // net::HttpServer::Delegate | |
73 void OnConnect(int connection_id) override { | |
74 } | |
75 | |
76 void OnClose(int connection_id) override { | |
77 } | |
78 | |
79 void OnHttpRequest( | |
80 int connection_id, const net::HttpServerRequestInfo& info) override { | |
81 | |
82 // FIXME: We should use use a fancier lookup system more like what | |
83 // services/http_server/http_server.cc does with AddHandler. | |
84 if (info.path == "/reload") | |
85 Load(connection_id, url_); | |
86 else if (info.path == "/inspect") | |
87 Inspect(connection_id); | |
88 else if (info.path == "/quit") | |
89 Quit(connection_id); | |
90 else if (info.path == "/load") | |
91 Load(connection_id, info.data); | |
92 else if (info.path == "/start_profiling") | |
93 StartProfiling(connection_id); | |
94 else if (info.path == "/stop_profiling") | |
95 StopProfiling(connection_id); | |
96 else if (info.path == "/start_tracing") | |
97 StartTracing(connection_id); | |
98 else if (info.path == "/stop_tracing") | |
99 StopTracing(connection_id); | |
100 else | |
101 Help(info.path, connection_id); | |
102 } | |
103 | |
104 void OnWebSocketRequest( | |
105 int connection_id, const net::HttpServerRequestInfo& info) override { | |
106 Error(connection_id, "OnWebSocketRequest not implemented"); | |
107 } | |
108 | |
109 void OnWebSocketMessage( | |
110 int connection_id, const std::string& data) override { | |
111 Error(connection_id, "OnWebSocketMessage not implemented"); | |
112 } | |
113 | |
114 void Error(int connection_id, std::string message) { | |
115 web_server_->Send500(connection_id, message); | |
116 } | |
117 | |
118 void Respond(int connection_id, std::string response) { | |
119 // When sending tracing data back over the wire to the client, we can blow | |
120 // through the default send buffer size. | |
121 web_server_->SetSendBufferSize( | |
122 connection_id, std::max(kMinSendBufferSize, response.length())); | |
123 web_server_->Send200(connection_id, response, "text/plain"); | |
124 } | |
125 | |
126 void Help(std::string path, int connection_id) { | |
127 std::string help = base::StringPrintf("Sky Debugger running on port %d\n" | |
128 "Supported URLs:\n" | |
129 "/reload -- Reload the current page\n" | |
130 "/quit -- Quit\n" | |
131 "/load -- Load a new URL, url in POST body.\n", | |
132 command_port_); | |
133 if (path != "/") | |
134 help = "Unknown path: " + path + "\n\n" + help; | |
135 Respond(connection_id, help); | |
136 } | |
137 | |
138 void Load(int connection_id, std::string url) { | |
139 url_ = url; | |
140 Reload(); | |
141 std::string response = std::string("Loaded ") + url + "\n"; | |
142 Respond(connection_id, response); | |
143 } | |
144 | |
145 void Reload() { | |
146 debugger_->NavigateToURL(url_); | |
147 } | |
148 | |
149 void Inspect(int connection_id) { | |
150 debugger_->InjectInspector(); | |
151 Respond(connection_id, | |
152 "Open the following URL in Chrome:\n" | |
153 "chrome-devtools://devtools/bundled/devtools.html?ws=localhost:9898\n"); | |
154 } | |
155 | |
156 void Quit(int connection_id) { | |
157 debugger_->Shutdown(); | |
158 } | |
159 | |
160 void StartTracing(int connection_id) { | |
161 if (is_tracing_) { | |
162 Error(connection_id, "Already tracing. Use stop_tracing to stop.\n"); | |
163 return; | |
164 } | |
165 | |
166 is_tracing_ = true; | |
167 mojo::DataPipe pipe; | |
168 tracing_->Start(pipe.producer_handle.Pass(), mojo::String("*")); | |
169 trace_collector_.reset(new TraceCollector(pipe.consumer_handle.Pass())); | |
170 Respond(connection_id, "Starting trace (type 'stop_tracing' to stop)\n"); | |
171 } | |
172 | |
173 void StopTracing(int connection_id) { | |
174 if (!is_tracing_) { | |
175 Error(connection_id, "Not tracing yet. Use start_tracing to start.\n"); | |
176 return; | |
177 } | |
178 | |
179 is_tracing_ = false; | |
180 tracing_->StopAndFlush(); | |
181 trace_collector_->GetTrace(base::Bind( | |
182 &Prompt::OnTraceAvailable, base::Unretained(this), connection_id)); | |
183 } | |
184 | |
185 void OnTraceAvailable(int connection_id, std::string trace) { | |
186 trace_collector_.reset(); | |
187 Respond(connection_id, trace); | |
188 } | |
189 | |
190 void StartProfiling(int connection_id) { | |
191 #if !defined(NDEBUG) || !defined(ENABLE_PROFILING) | |
192 Error(connection_id, | |
193 "Profiling requires is_debug=false and enable_profiling=true"); | |
194 return; | |
195 #else | |
196 base::debug::StartProfiling("sky_viewer.pprof"); | |
197 Respond(connection_id, "Starting profiling (stop with 'stop_profiling')"); | |
198 #endif | |
199 } | |
200 | |
201 void StopProfiling(int connection_id) { | |
202 if (!base::debug::BeingProfiled()) { | |
203 Error(connection_id, "Profiling not started"); | |
204 return; | |
205 } | |
206 base::debug::StopProfiling(); | |
207 Respond(connection_id, "Stopped profiling"); | |
208 } | |
209 | |
210 bool is_tracing_; | |
211 DebuggerPtr debugger_; | |
212 tracing::TraceCoordinatorPtr tracing_; | |
213 std::string url_; | |
214 base::WeakPtrFactory<Prompt> weak_ptr_factory_; | |
215 scoped_ptr<net::HttpServer> web_server_; | |
216 uint32_t command_port_; | |
217 | |
218 scoped_ptr<TraceCollector> trace_collector_; | |
219 | |
220 DISALLOW_COPY_AND_ASSIGN(Prompt); | |
221 }; | |
222 | |
223 } // namespace debugger | |
224 } // namespace sky | |
225 | |
226 MojoResult MojoMain(MojoHandle shell_handle) { | |
227 mojo::ApplicationRunnerChromium runner(new sky::debugger::Prompt); | |
228 runner.set_message_loop_type(base::MessageLoop::TYPE_IO); | |
229 return runner.Run(shell_handle); | |
230 } | |
OLD | NEW |