| 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 |