Index: sky/tools/debugger/debugger.cc |
diff --git a/sky/tools/debugger/debugger.cc b/sky/tools/debugger/debugger.cc |
index 1bf43e42879a9dd4b1dc44fa6b903048a05a955d..4056b500950cef013257653c80916dee4b32ce2e 100644 |
--- a/sky/tools/debugger/debugger.cc |
+++ b/sky/tools/debugger/debugger.cc |
@@ -2,132 +2,217 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-#include "sky/tools/debugger/debugger.h" |
- |
-#include "services/window_manager/basic_focus_rules.h" |
+#include <algorithm> |
+ |
+#include "base/bind.h" |
+#include "base/debug/profiler.h" |
+#include "base/memory/weak_ptr.h" |
+#include "base/strings/string_number_conversions.h" |
+#include "base/strings/stringprintf.h" |
+#include "mojo/application/application_runner_chromium.h" |
+#include "mojo/public/c/system/main.h" |
+#include "mojo/public/cpp/application/application_delegate.h" |
+#include "mojo/public/cpp/application/application_impl.h" |
+#include "mojo/services/window_manager/public/interfaces/window_manager.mojom.h" |
+#include "net/base/net_errors.h" |
+#include "net/server/http_server.h" |
+#include "net/server/http_server_request_info.h" |
+#include "net/socket/tcp_server_socket.h" |
+#include "services/tracing/tracing.mojom.h" |
+#include "sky/tools/debugger/trace_collector.h" |
namespace sky { |
namespace debugger { |
+namespace { |
-SkyDebugger::SkyDebugger() |
- : window_manager_app_(new window_manager::WindowManagerApp(this, this)), |
- root_(nullptr), |
- content_(nullptr), |
- navigator_host_factory_(this), |
- weak_factory_(this) { |
- exposed_services_impl_.AddService(&navigator_host_factory_); |
-} |
- |
-SkyDebugger::~SkyDebugger() { |
+const size_t kMinSendBufferSize = 1024 * 1024; |
} |
-base::WeakPtr<SkyDebugger> SkyDebugger::GetWeakPtr() { |
- return weak_factory_.GetWeakPtr(); |
-} |
+class SkyDebugger : public mojo::ApplicationDelegate, |
+ public net::HttpServer::Delegate { |
+ public: |
+ SkyDebugger() : is_tracing_(false), weak_ptr_factory_(this) {} |
+ virtual ~SkyDebugger() {} |
+ |
+ private: |
+ // Overridden from mojo::ApplicationDelegate: |
+ virtual void Initialize(mojo::ApplicationImpl* app) override { |
+ app->ConnectToService("mojo:tracing", &tracing_); |
+ // Format: --args-for="app_url command_port" |
+ if (app->args().size() < 2) { |
+ LOG(ERROR) << "--args-for required to specify command_port"; |
+ mojo::ApplicationImpl::Terminate(); |
+ return; |
+ } |
+ |
+ base::StringToUint(app->args()[1], &command_port_); |
+ |
+ scoped_ptr<net::ServerSocket> server_socket( |
+ new net::TCPServerSocket(NULL, net::NetLog::Source())); |
+ int result = |
+ server_socket->ListenWithAddressAndPort("0.0.0.0", command_port_, 1); |
+ if (result != net::OK) { |
+ LOG(ERROR) << "Failed to bind to port " << command_port_ |
+ << " skydb commands will not work."; |
+ mojo::ApplicationImpl::Terminate(); |
+ return; |
+ } |
+ web_server_.reset(new net::HttpServer(server_socket.Pass(), this)); |
+ |
+ app->ConnectToService("mojo:window_manager", &window_manager_); |
+ } |
-void SkyDebugger::Initialize(mojo::ApplicationImpl* app) { |
- window_manager_app_->Initialize(app); |
- app->ConnectToApplication("mojo:sky_debugger_prompt"); |
+ virtual bool ConfigureIncomingConnection( |
+ mojo::ApplicationConnection* connection) override { |
+ return true; |
+ } |
- // Format: --args-for="app_url default_url" |
- if (app->args().size() > 1) |
- default_url_ = app->args()[1]; |
-} |
+ // net::HttpServer::Delegate |
+ void OnConnect(int connection_id) override {} |
+ |
+ void OnClose(int connection_id) override {} |
+ |
+ void OnHttpRequest(int connection_id, |
+ const net::HttpServerRequestInfo& info) override { |
+ // FIXME: We should use use a fancier lookup system more like what |
+ // services/http_server/http_server.cc does with AddHandler. |
+ if (info.path == "/reload") |
+ Load(connection_id, url_); |
+ else if (info.path == "/quit") |
+ Quit(connection_id); |
+ else if (info.path == "/load") |
+ Load(connection_id, info.data); |
+ else if (info.path == "/start_profiling") |
+ StartProfiling(connection_id); |
+ else if (info.path == "/stop_profiling") |
+ StopProfiling(connection_id); |
+ else if (info.path == "/start_tracing") |
+ StartTracing(connection_id); |
+ else if (info.path == "/stop_tracing") |
+ StopTracing(connection_id); |
+ else |
+ Help(info.path, connection_id); |
+ } |
-bool SkyDebugger::ConfigureIncomingConnection( |
- mojo::ApplicationConnection* connection) { |
- window_manager_app_->ConfigureIncomingConnection(connection); |
- connection->AddService(this); |
+ void OnWebSocketRequest(int connection_id, |
+ const net::HttpServerRequestInfo& info) override { |
+ Error(connection_id, "OnWebSocketRequest not implemented"); |
+ } |
- if (!default_url_.empty()) |
- NavigateToURL(default_url_); // Schedule a navigation in the new embedding. |
- return true; |
-} |
+ void OnWebSocketMessage(int connection_id, const std::string& data) override { |
+ Error(connection_id, "OnWebSocketMessage not implemented"); |
+ } |
-bool SkyDebugger::ConfigureOutgoingConnection( |
- mojo::ApplicationConnection* connection) { |
- window_manager_app_->ConfigureOutgoingConnection(connection); |
- connection->AddService(this); |
- return true; |
-} |
+ void Error(int connection_id, std::string message) { |
+ web_server_->Send500(connection_id, message); |
+ } |
-void SkyDebugger::OnEmbed( |
- mojo::View* root, |
- mojo::InterfaceRequest<mojo::ServiceProvider> services, |
- mojo::ServiceProviderPtr exposed_services) { |
- root_ = root; |
- root_->AddObserver(this); |
+ void Respond(int connection_id, std::string response) { |
+ // When sending tracing data back over the wire to the client, we can blow |
+ // through the default send buffer size. |
+ web_server_->SetSendBufferSize( |
+ connection_id, std::max(kMinSendBufferSize, response.length())); |
+ web_server_->Send200(connection_id, response, "text/plain"); |
+ } |
- window_manager_app_->SetViewportSize(gfx::Size(320, 640)); |
+ void Help(std::string path, int connection_id) { |
+ std::string help = base::StringPrintf( |
+ "Sky Debugger running on port %d\n" |
+ "Supported URLs:\n" |
+ "/reload -- Reload the current page\n" |
+ "/quit -- Quit\n" |
+ "/load -- Load a new URL, url in POST body.\n", |
+ command_port_); |
+ if (path != "/") |
+ help = "Unknown path: " + path + "\n\n" + help; |
+ Respond(connection_id, help); |
+ } |
- content_ = root->view_manager()->CreateView(); |
- content_->SetBounds(root_->bounds()); |
- root_->AddChild(content_); |
- content_->SetVisible(true); |
+ void Load(int connection_id, std::string url) { |
+ url_ = url; |
+ Reload(); |
+ std::string response = std::string("Loaded ") + url + "\n"; |
+ Respond(connection_id, response); |
+ } |
- window_manager_app_->InitFocus( |
- make_scoped_ptr(new window_manager::BasicFocusRules(root_))); |
+ void Reload() { |
+ // SimpleWindowManager will wire up necessary services on our behalf. |
+ window_manager_->Embed(url_, nullptr, nullptr); |
+ } |
- if (!pending_url_.empty()) |
- NavigateToURL(pending_url_); |
-} |
+ void Quit(int connection_id) { |
+ // TODO(eseidel): We should orderly shutdown once mojo can. |
+ exit(0); |
+ } |
-void SkyDebugger::Embed(const mojo::String& url, |
- mojo::InterfaceRequest<mojo::ServiceProvider> services, |
- mojo::ServiceProviderPtr exposed_services) { |
- content_->Embed(url, nullptr, nullptr); |
-} |
+ void StartTracing(int connection_id) { |
+ if (is_tracing_) { |
+ Error(connection_id, "Already tracing. Use stop_tracing to stop.\n"); |
+ return; |
+ } |
+ |
+ is_tracing_ = true; |
+ mojo::DataPipe pipe; |
+ tracing_->Start(pipe.producer_handle.Pass(), mojo::String("*")); |
+ trace_collector_.reset(new TraceCollector(pipe.consumer_handle.Pass())); |
+ Respond(connection_id, "Starting trace (type 'stop_tracing' to stop)\n"); |
+ } |
-void SkyDebugger::OnViewManagerDisconnected(mojo::ViewManager* view_manager) { |
- root_ = nullptr; |
-} |
+ void StopTracing(int connection_id) { |
+ if (!is_tracing_) { |
+ Error(connection_id, "Not tracing yet. Use start_tracing to start.\n"); |
+ return; |
+ } |
-void SkyDebugger::OnViewDestroyed(mojo::View* view) { |
- view->RemoveObserver(this); |
-} |
+ is_tracing_ = false; |
+ tracing_->StopAndFlush(); |
+ trace_collector_->GetTrace(base::Bind( |
+ &SkyDebugger::OnTraceAvailable, base::Unretained(this), connection_id)); |
+ } |
-void SkyDebugger::OnViewBoundsChanged(mojo::View* view, |
- const mojo::Rect& old_bounds, |
- const mojo::Rect& new_bounds) { |
- content_->SetBounds(new_bounds); |
-} |
+ void OnTraceAvailable(int connection_id, std::string trace) { |
+ trace_collector_.reset(); |
+ Respond(connection_id, trace); |
+ } |
-void SkyDebugger::Create(mojo::ApplicationConnection* connection, |
- mojo::InterfaceRequest<Debugger> request) { |
- mojo::WeakBindToRequest(this, &request); |
-} |
+ void StartProfiling(int connection_id) { |
+#if !defined(NDEBUG) || !defined(ENABLE_PROFILING) |
+ Error(connection_id, |
+ "Profiling requires is_debug=false and enable_profiling=true"); |
+ return; |
+#else |
+ base::debug::StartProfiling("sky_viewer.pprof"); |
+ Respond(connection_id, "Starting profiling (stop with 'stop_profiling')"); |
+#endif |
+ } |
-void SkyDebugger::NavigateToURL(const mojo::String& url) { |
- // We can get Navigate commands before we've actually been |
- // embedded into the view and content_ created. |
- // Just save the last one. |
- if (content_) { |
- mojo::ServiceProviderPtr exposed_services; |
- exposed_services_impl_.Bind(GetProxy(&exposed_services)); |
- content_->Embed(url, GetProxy(&viewer_services_), exposed_services.Pass()); |
- } else { |
- pending_url_ = url; |
+ void StopProfiling(int connection_id) { |
+ if (!base::debug::BeingProfiled()) { |
+ Error(connection_id, "Profiling not started"); |
+ return; |
+ } |
+ base::debug::StopProfiling(); |
+ Respond(connection_id, "Stopped profiling"); |
} |
-} |
-void SkyDebugger::Shutdown() { |
- // Make sure we shut down mojo before quitting the message loop or things |
- // like blink::shutdown() may try to talk to the message loop and crash. |
- window_manager_app_.reset(); |
- |
- // TODO(eseidel): This still hits an X11 error which I don't understand |
- // "X Error of failed request: GLXBadDrawable", crbug.com/430581 |
- mojo::ApplicationImpl::Terminate(); |
- // TODO(eseidel): REMOVE THIS, temporarily fast-exit now to stop confusing |
- // folks with exit-time crashes due to GLXBadDrawable above. |
- exit(0); |
-} |
+ bool is_tracing_; |
+ mojo::WindowManagerPtr window_manager_; |
+ tracing::TraceCoordinatorPtr tracing_; |
+ std::string url_; |
+ base::WeakPtrFactory<SkyDebugger> weak_ptr_factory_; |
+ scoped_ptr<net::HttpServer> web_server_; |
+ uint32_t command_port_; |
-void SkyDebugger::InjectInspector() { |
- InspectorServicePtr inspector_service; |
- mojo::ConnectToService(viewer_services_.get(), &inspector_service); |
- inspector_service->Inject(); |
-} |
+ scoped_ptr<TraceCollector> trace_collector_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(SkyDebugger); |
+}; |
} // namespace debugger |
} // namespace sky |
+ |
+MojoResult MojoMain(MojoHandle shell_handle) { |
+ mojo::ApplicationRunnerChromium runner(new sky::debugger::SkyDebugger); |
+ runner.set_message_loop_type(base::MessageLoop::TYPE_IO); |
+ return runner.Run(shell_handle); |
+} |