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