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