Commit 1fd1a524 authored by cpu@chromium.org's avatar cpu@chromium.org

Redo: Adding a mojo interface to the mojo spy

This is the same as
https://codereview.chromium.org/284743002

Which was reverted because a mojo refactoring won the checking race
and I did not notice the revert.

Original message:

Adding more guts to the mojo spy.

This CL fleshes out the basic architecture of the spy. Introducing SpyServerImpl and enough code to operate the "start" and "stop" commands of the front-end.

Still a quite a few loose ends, specially on teardown.

I updated the design doc to reflect this CL:
https://docs.google.com/a/chromium.org/document/d/11FKYXf9mSohlsgl4JmGlyWE1ScX3DKdssdjub63tkwA/edit?usp=sharing

TBR=darin
BUG=360188
TEST=manual via test/spy_repl_test.html

Review URL: https://codereview.chromium.org/379753002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@281995 0039d316-1c4b-4281-b951-d872f2087c98
parent 038e8b8b
......@@ -508,9 +508,16 @@
'../url/url.gyp:url_lib',
'mojo_service_manager',
],
'variables': {
'mojom_base_output_dir': 'mojo',
},
'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
'sources': [
'spy/public/spy.mojom',
'spy/spy.cc',
'spy/spy.h',
'spy/spy_server_impl.h',
'spy/spy_server_impl.cc',
'spy/websocket_server.cc',
'spy/websocket_server.h',
],
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
module mojo.spy_api {
enum Result {
ALL_OK,
INTERNAL_ERROR,
INVALID_ID,
NO_MORE_IDS,
INVALID_CALL,
INVALID_PARAMS,
BAD_STATE,
RESOURCE_LIMIT
};
struct Version {
uint32 v_major;
uint32 v_minor;
};
enum ConnectionOptions {
SKIP,
PAUSE,
RESUME,
PEEK_MESSAGES
};
struct Message {
uint32 id;
uint32 time;
uint8[] data;
};
[Client=SpyClient]
interface SpyServer {
StartSession(Version version) => (Result result, string name);
StopSession() => (Result result);
TrackConnection(uint32 id, ConnectionOptions options) => (Result result);
};
interface SpyClient {
OnFatalError(Result result);
OnSessionEnd(Result result);
OnClientConnection(string name, uint32 id, ConnectionOptions options);
OnMessage(Message message);
};
}
......@@ -4,10 +4,13 @@
#include "mojo/spy/spy.h"
#include <vector>
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/threading/thread.h"
......@@ -15,7 +18,10 @@
#include "mojo/public/cpp/system/core.h"
#include "mojo/service_manager/service_manager.h"
#include "mojo/spy/public/spy.mojom.h"
#include "mojo/spy/spy_server_impl.h"
#include "mojo/spy/websocket_server.h"
#include "url/gurl.h"
namespace {
......@@ -33,11 +39,9 @@ void CloseHandles(MojoHandle* handles, size_t count) {
class MessageProcessor :
public base::RefCountedThreadSafe<MessageProcessor> {
public:
MessageProcessor()
: last_result_(MOJO_RESULT_OK),
bytes_transfered_(0) {
message_count_[0] = 0;
message_count_[1] = 0;
handle_count_[0] = 0;
......@@ -107,24 +111,30 @@ class MessageProcessor :
}
private:
friend class base::RefCountedThreadSafe<MessageProcessor>;
virtual ~MessageProcessor() {}
bool CheckResult(MojoResult mr) {
if (mr == MOJO_RESULT_OK)
return true;
last_result_ = mr;
return false;
}
MojoResult last_result_;
uint32_t bytes_transfered_;
uint32_t message_count_[2];
uint32_t handle_count_[2];
friend class base::RefCountedThreadSafe<MessageProcessor>;
virtual ~MessageProcessor() {}
bool CheckResult(MojoResult mr) {
if (mr == MOJO_RESULT_OK)
return true;
last_result_ = mr;
return false;
}
MojoResult last_result_;
uint32_t bytes_transfered_;
uint32_t message_count_[2];
uint32_t handle_count_[2];
};
// In charge of intercepting access to the service manager.
class SpyInterceptor : public mojo::ServiceManager::Interceptor {
public:
explicit SpyInterceptor(scoped_refptr<mojo::SpyServerImpl> spy_server)
: spy_server_(spy_server),
proxy_(base::MessageLoopProxy::current()) {
}
private:
virtual mojo::ServiceProviderPtr OnConnectToClient(
const GURL& url, mojo::ServiceProviderPtr real_client) OVERRIDE {
......@@ -156,15 +166,21 @@ class SpyInterceptor : public mojo::ServiceManager::Interceptor {
bool MustIntercept(const GURL& url) {
// TODO(cpu): manage who and when to intercept.
proxy_->PostTask(
FROM_HERE,
base::Bind(&mojo::SpyServerImpl::OnIntercept, spy_server_, url));
return true;
}
scoped_refptr<mojo::SpyServerImpl> spy_server_;
scoped_refptr<base::MessageLoopProxy> proxy_;
};
spy::WebSocketServer* ws_server = NULL;
mojo::WebSocketServer* ws_server = NULL;
void StartServer(int port) {
void StartWebServer(int port, mojo::ScopedMessagePipeHandle pipe) {
// TODO(cpu) figure out lifetime of the server. See Spy() dtor.
ws_server = new spy::WebSocketServer(port);
ws_server = new mojo::WebSocketServer(port, pipe.Pass());
ws_server->Start();
}
......@@ -199,18 +215,23 @@ namespace mojo {
Spy::Spy(mojo::ServiceManager* service_manager, const std::string& options) {
SpyOptions spy_options = ProcessOptions(options);
spy_server_ = new SpyServerImpl();
// Start the tread what will accept commands from the frontend.
control_thread_.reset(new base::Thread("mojo_spy_control_thread"));
base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
control_thread_->StartWithOptions(thread_options);
control_thread_->message_loop_proxy()->PostTask(
FROM_HERE, base::Bind(&StartServer, spy_options.websocket_port));
FROM_HERE, base::Bind(&StartWebServer,
spy_options.websocket_port,
base::Passed(spy_server_->ServerPipe())));
// Start intercepting mojo services.
service_manager->SetInterceptor(new SpyInterceptor());
service_manager->SetInterceptor(new SpyInterceptor(spy_server_));
}
Spy::~Spy(){
Spy::~Spy() {
// TODO(cpu): Do not leak the interceptor. Lifetime between the
// service_manager and the spy is still unclear hence the leak.
}
......
......@@ -6,15 +6,17 @@
#define MOJO_SPY_SPY_H_
#include <string>
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
namespace base {
class Thread;
class Thread;
}
namespace mojo {
class ServiceManager;
class SpyServerImpl;
// mojo::Spy is a troubleshooting and debugging aid. It helps tracking
// the mojo system core activities like messages, service creation, etc.
......@@ -31,8 +33,9 @@ class Spy {
~Spy();
private:
// This thread runs the code that talks to the frontend.
scoped_ptr<base::Thread> control_thread_;
scoped_refptr<SpyServerImpl> spy_server_;
// This thread runs the code that talks to the frontend.
scoped_ptr<base::Thread> control_thread_;
};
} // namespace mojo
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/spy/spy_server_impl.h"
#include "mojo/public/cpp/system/core.h"
namespace {
bool NextId(uint32_t* out_id) {
static uint32_t id = 1;
if (!++id)
return false;
*out_id = id;
return true;
}
} // namespace
namespace mojo {
struct SpyServerImpl::Item {
enum Type {
kServiceIntercept,
kMessage
};
uint32_t id;
Type type;
Item(uint32_t id, Type type) : id(id), type(type) {}
};
SpyServerImpl::SpyServerImpl() : has_session_(false) {
BindToPipe(this, pipe_.handle0.Pass());
}
SpyServerImpl::~SpyServerImpl() {
}
void SpyServerImpl::StartSession(
spy_api::VersionPtr version,
const mojo::Callback<void(spy_api::Result, mojo::String)>& callback) {
if (has_session_) {
callback.Run(spy_api::RESOURCE_LIMIT, "");
return;
}
callback.Run(spy_api::ALL_OK, "session 0");
has_session_ = true;
}
void SpyServerImpl::StopSession(
const mojo::Callback<void(spy_api::Result)>& callback) {
if (!has_session_) {
callback.Run(spy_api::INVALID_CALL);
return;
}
callback.Run(spy_api::ALL_OK);
has_session_ = false;
}
void SpyServerImpl::TrackConnection(
uint32_t id,
spy_api::ConnectionOptions options,
const mojo::Callback<void(spy_api::Result)>& callback) {
}
void SpyServerImpl::OnConnectionError() {
// Pipe got disconnected.
}
void SpyServerImpl::OnIntercept(const GURL& url) {
if (!has_session_)
return;
uint32_t id;
if (!NextId(&id)) {
client()->OnFatalError(spy_api::NO_MORE_IDS);
return;
}
items_[id] = new Item(id, Item::kServiceIntercept);
client()->OnClientConnection(
url.possibly_invalid_spec(), id, spy_api::PEEK_MESSAGES);
}
ScopedMessagePipeHandle SpyServerImpl::ServerPipe() {
return ScopedMessagePipeHandle(pipe_.handle1.Pass()).Pass();
}
} // namespace mojo
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_SPY_SPY_SERVER_IMPL_H_
#define MOJO_SPY_SPY_SERVER_IMPL_H_
#include <map>
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "mojo/public/cpp/system/core.h"
#include "mojo/spy/public/spy.mojom.h"
#include "url/gurl.h"
namespace mojo {
class SpyServerImpl :
public base::RefCounted<SpyServerImpl>,
public InterfaceImpl<spy_api::SpyServer> {
public:
SpyServerImpl();
// spy_api::SpyServer implementation.
virtual void StartSession(
spy_api::VersionPtr version,
const mojo::Callback<void(spy_api::Result,
mojo::String)>& callback) OVERRIDE;
virtual void StopSession(
const mojo::Callback<void(spy_api::Result)>& callback) OVERRIDE;
virtual void TrackConnection(
uint32_t id,
spy_api::ConnectionOptions options,
const mojo::Callback<void(spy_api::Result)>& callback) OVERRIDE;
virtual void OnConnectionError() OVERRIDE;
// SpyServerImpl own methods.
void OnIntercept(const GURL& url);
ScopedMessagePipeHandle ServerPipe();
private:
friend class base::RefCounted<SpyServerImpl>;
virtual ~SpyServerImpl();
// Item models the entities that we track by IDs.
struct Item;
MessagePipe pipe_;
bool has_session_;
std::map<uint32_t, Item*> items_;
};
} // namespace mojo
#endif // MOJO_SPY_SPY_SERVER_IMPL_H_
......@@ -4,20 +4,29 @@
#include "mojo/spy/websocket_server.h"
#include <string>
#include "base/bind.h"
#include "base/strings/stringprintf.h"
#include "mojo/public/cpp/bindings/message.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/server/http_server_request_info.h"
#include "net/server/http_server_response_info.h"
#include "net/socket/tcp_listen_socket.h"
namespace spy {
namespace mojo {
const int kNotConnected = -1;
WebSocketServer::WebSocketServer(int port)
: port_(port), connection_id_(kNotConnected) {
WebSocketServer::WebSocketServer(int port,
mojo::ScopedMessagePipeHandle server_pipe)
: port_(port),
connection_id_(kNotConnected),
spy_server_(MakeProxy<spy_api::SpyServer>(server_pipe.Pass())) {
spy_server_.set_client(this);
}
WebSocketServer::~WebSocketServer() {
......@@ -25,9 +34,9 @@ WebSocketServer::~WebSocketServer() {
bool WebSocketServer::Start() {
net::TCPListenSocketFactory factory("0.0.0.0", port_);
server_ = new net::HttpServer(factory, this);
web_server_ = new net::HttpServer(factory, this);
net::IPEndPoint address;
int error = server_->GetLocalAddress(&address);
int error = web_server_->GetLocalAddress(&address);
port_ = address.port();
return (error == net::OK);
}
......@@ -35,7 +44,7 @@ bool WebSocketServer::Start() {
void WebSocketServer::OnHttpRequest(
int connection_id,
const net::HttpServerRequestInfo& info) {
server_->Send500(connection_id, "websockets protocol only");
web_server_->Send500(connection_id, "websockets protocol only");
}
void WebSocketServer::OnWebSocketRequest(
......@@ -45,26 +54,63 @@ void WebSocketServer::OnWebSocketRequest(
// Reject connection since we already have our client.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&net::HttpServer::Close, server_, connection_id));
base::Bind(&net::HttpServer::Close, web_server_, connection_id));
return;
}
// Accept the connection.
server_->AcceptWebSocket(connection_id, info);
web_server_->AcceptWebSocket(connection_id, info);
connection_id_ = connection_id;
}
void WebSocketServer::OnWebSocketMessage(
int connection_id,
const std::string& data) {
// TODO(cpu): remove this test code soon.
if (data == "\"hello\"")
server_->SendOverWebSocket(connection_id, "\"hi there!\"");
if (data == "\"start\"") {
spy_api::VersionPtr ver = spy_api::Version::New();
ver->v_major = 0;
ver->v_minor = 1;
spy_server_->StartSession(
ver.Pass(),
base::Bind(&WebSocketServer::OnStartSession, base::Unretained(this)));
} else if (data == "\"stop\"") {
spy_server_->StopSession(
base::Bind(&WebSocketServer::OnSessionEnd, base::Unretained(this)));
}
}
void WebSocketServer::OnFatalError(spy_api::Result result) {
web_server_->SendOverWebSocket(connection_id_, "\"fatal error\"");
}
void WebSocketServer::OnClose(
int connection_id) {
if (connection_id == connection_id_)
connection_id_ = kNotConnected;
if (connection_id != connection_id_)
return;
connection_id_ = kNotConnected;
spy_server_->StopSession(
base::Bind(&WebSocketServer::OnSessionEnd, base::Unretained(this)));
}
void WebSocketServer::OnSessionEnd(spy_api::Result result) {
// Called when the spy session (not the websocket) ends.
}
void WebSocketServer::OnClientConnection(
const mojo::String& name,
uint32_t id,
spy_api::ConnectionOptions options) {
std::string cc("\"");
cc += name.To<std::string>() + "\"";
web_server_->SendOverWebSocket(connection_id_, cc);
}
void WebSocketServer::OnMessage(spy_api::MessagePtr message) {
}
void WebSocketServer::OnStartSession(spy_api::Result, mojo::String) {
web_server_->SendOverWebSocket(connection_id_, "\"ok start\"");
}
} // namespace spy
} // namespace mojo
......@@ -5,14 +5,18 @@
#ifndef MOJO_SPY_WEBSOCKET_SERVER_H_
#define MOJO_SPY_WEBSOCKET_SERVER_H_
#include <string>
#include "mojo/spy/public/spy.mojom.h"
#include "net/server/http_server.h"
namespace spy {
namespace mojo {
class WebSocketServer : public net::HttpServer::Delegate {
class WebSocketServer : public net::HttpServer::Delegate,
public spy_api::SpyClient {
public:
// Pass 0 in |port| to listen in one available port.
explicit WebSocketServer(int port);
explicit WebSocketServer(int port, ScopedMessagePipeHandle server_pipe);
virtual ~WebSocketServer();
// Begin accepting HTTP requests. Must be called from an IO MessageLoop.
bool Start();
......@@ -32,13 +36,27 @@ class WebSocketServer : public net::HttpServer::Delegate {
const std::string& data) OVERRIDE;
virtual void OnClose(int connection_id) OVERRIDE;
// Overriden form spy_api::SpyClient.
virtual void OnFatalError(spy_api::Result result) OVERRIDE;
virtual void OnSessionEnd(spy_api::Result result) OVERRIDE;
virtual void OnClientConnection(
const mojo::String& name,
uint32_t id,
spy_api::ConnectionOptions options) OVERRIDE;
virtual void OnMessage(spy_api::MessagePtr message) OVERRIDE;
// Callbacks from calling spy_api::SpyServer.
void OnStartSession(spy_api::Result, mojo::String);
private:
int port_;
int connection_id_;
scoped_refptr<net::HttpServer> server_;
scoped_refptr<net::HttpServer> web_server_;
spy_api::SpyServerPtr spy_server_;
DISALLOW_COPY_AND_ASSIGN(WebSocketServer);
};
} // namespace spy
} // namespace mojo
#endif // MOJO_SPY_WEBSOCKET_SERVER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment