Make skydb just a ctl-style helper (e.g. apachectl)
This makes it much easier to work with a device where we don't expect a persistent commandline session. Prompt no longer actually makes a prompt but rather just runs an http server. This is just a v1 patch. It doesn't work with android yet and it isn't smart enough to allow you to run more than one copy of prompt.cc at the same time (since you can't control the port it listens on). If you have a second copy of prompt runnning (or anything else bound to port 7777) it will just crash. We could make this a lot better, including splitting out the pid-file tracking for sky_server into sky_server instead of having skydb manage it. R=ojan@chromium.org, abarth@chromium.org BUG= Review URL: https://codereview.chromium.org/840973002
This commit is contained in:
parent
6d658c8c95
commit
5f587b2878
@ -16,6 +16,8 @@ mojo_native_application("prompt") {
|
||||
"//mojo/application",
|
||||
"//mojo/public/cpp/bindings",
|
||||
"//mojo/public/cpp/utility",
|
||||
"//net",
|
||||
"//net:http_server",
|
||||
"//services/tracing:bindings",
|
||||
"//sky/tools/debugger:bindings",
|
||||
"//sky/viewer:bindings",
|
||||
|
@ -8,29 +8,17 @@
|
||||
#include "mojo/public/c/system/main.h"
|
||||
#include "mojo/public/cpp/application/application_delegate.h"
|
||||
#include "mojo/public/cpp/application/application_impl.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/debugger.mojom.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace sky {
|
||||
namespace debugger {
|
||||
namespace {
|
||||
|
||||
std::string GetCommand() {
|
||||
std::cout << "(skydb) ";
|
||||
std::cout.flush();
|
||||
|
||||
std::string command;
|
||||
std::getline(std::cin, command);
|
||||
// Any errors (including eof) just quit the debugger:
|
||||
if (!std::cin.good())
|
||||
command = 'q';
|
||||
return command;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Prompt : public mojo::ApplicationDelegate {
|
||||
class Prompt : public mojo::ApplicationDelegate, public net::HttpServer::Delegate {
|
||||
public:
|
||||
Prompt()
|
||||
: is_tracing_(false),
|
||||
@ -49,6 +37,12 @@ class Prompt : public mojo::ApplicationDelegate {
|
||||
url_ = "https://raw.githubusercontent.com/domokit/mojo/master/sky/"
|
||||
"examples/home.sky";
|
||||
}
|
||||
scoped_ptr<net::ServerSocket> server_socket(
|
||||
new net::TCPServerSocket(NULL, net::NetLog::Source()));
|
||||
// FIXME: This port needs to be configurable, as-is we can only run
|
||||
// one copy of mojo_shell with sky at a time!
|
||||
server_socket->ListenWithAddressAndPort("0.0.0.0", 7777, 1);
|
||||
web_server_.reset(new net::HttpServer(server_socket.Pass(), this));
|
||||
}
|
||||
|
||||
virtual bool ConfigureIncomingConnection(
|
||||
@ -56,93 +50,86 @@ class Prompt : public mojo::ApplicationDelegate {
|
||||
connection->ConnectToService(&debugger_);
|
||||
std::cout << "Loading " << url_ << std::endl;
|
||||
Reload();
|
||||
#if !defined(OS_ANDROID)
|
||||
// FIXME: To support device-centric development we need to re-write
|
||||
// prompt.cc to just be a server and have all the command handling move
|
||||
// to python (skydb). prompt.cc would just run until told to quit.
|
||||
// If we don't comment this out then prompt.cc just quits when run headless
|
||||
// as it immediately recieves EOF which it treats as quit.
|
||||
ScheduleWaitForInput();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExecuteCommand(const std::string& command) {
|
||||
if (command == "help" || command == "h") {
|
||||
PrintHelp();
|
||||
return true;
|
||||
}
|
||||
if (command == "trace") {
|
||||
ToggleTracing();
|
||||
return true;
|
||||
}
|
||||
if (command == "reload" || command == "r") {
|
||||
Reload();
|
||||
return true;
|
||||
}
|
||||
if (command == "inspect") {
|
||||
Inspect();
|
||||
return true;
|
||||
}
|
||||
if (command == "quit" || command == "q") {
|
||||
Quit();
|
||||
return true;
|
||||
}
|
||||
if (command.size() == 1) {
|
||||
std::cout << "Unknown command: " << command << std::endl;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
// net::HttpServer::Delegate
|
||||
void OnConnect(int connection_id) override {
|
||||
}
|
||||
|
||||
void WaitForInput() {
|
||||
std::string command = GetCommand();
|
||||
void OnClose(int connection_id) override {
|
||||
}
|
||||
|
||||
if (!ExecuteCommand(command)) {
|
||||
if (command.size() > 0) {
|
||||
url_ = command;
|
||||
Reload();
|
||||
}
|
||||
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 == "/trace")
|
||||
ToggleTracing(connection_id);
|
||||
else if (info.path == "/reload")
|
||||
Load(connection_id, url_);
|
||||
else if (info.path == "/inspect")
|
||||
Inspect(connection_id);
|
||||
else if (info.path == "/quit")
|
||||
Quit(connection_id);
|
||||
else if (info.path == "/load")
|
||||
Load(connection_id, info.data);
|
||||
else {
|
||||
Help(info.path, connection_id);
|
||||
}
|
||||
|
||||
ScheduleWaitForInput();
|
||||
}
|
||||
|
||||
void ScheduleWaitForInput() {
|
||||
base::MessageLoop::current()->PostTask(FROM_HERE,
|
||||
base::Bind(&Prompt::WaitForInput, weak_ptr_factory_.GetWeakPtr()));
|
||||
void OnWebSocketRequest(
|
||||
int connection_id, const net::HttpServerRequestInfo& info) override {
|
||||
web_server_->Send500(connection_id, "http only");
|
||||
}
|
||||
|
||||
void PrintHelp() {
|
||||
std::cout
|
||||
<< "Sky Debugger" << std::endl
|
||||
<< "============" << std::endl
|
||||
<< "Type a URL to load in the debugger, enter to reload." << std::endl
|
||||
<< "Commands: help -- Help" << std::endl
|
||||
<< " trace -- Capture a trace" << std::endl
|
||||
<< " reload -- Reload the current page" << std::endl
|
||||
<< " inspect -- Inspect the current page" << std::endl
|
||||
<< " quit -- Quit" << std::endl;
|
||||
void OnWebSocketMessage(
|
||||
int connection_id, const std::string& data) override {
|
||||
web_server_->Send500(connection_id, "http only");
|
||||
}
|
||||
|
||||
void Respond(int connection_id, std::string response) {
|
||||
web_server_->Send200(connection_id, response, "text/plain");
|
||||
}
|
||||
|
||||
void Help(std::string path, int connection_id) {
|
||||
std::string help = "Sky Debugger\n"
|
||||
"Supported URLs:\n"
|
||||
"/toggle_tracing -- Start/stop tracing\n"
|
||||
"/reload -- Reload the current page\n"
|
||||
"/inspect -- Start inspector server for current page\n"
|
||||
"/quit -- Quit\n"
|
||||
"/load -- Load a new URL, url in POST body.\n";
|
||||
if (path != "/")
|
||||
help = "Unknown path: " + path + "\n\n" + help;
|
||||
Respond(connection_id, help);
|
||||
}
|
||||
|
||||
void Load(int connection_id, std::string url) {
|
||||
url_ = url;
|
||||
Reload();
|
||||
Respond(connection_id, "OK\n");
|
||||
}
|
||||
|
||||
void Reload() {
|
||||
debugger_->NavigateToURL(url_);
|
||||
}
|
||||
|
||||
void Inspect() {
|
||||
void Inspect(int connection_id) {
|
||||
debugger_->InjectInspector();
|
||||
std::cout
|
||||
<< "Open the following URL in Chrome:" << std::endl
|
||||
<< "chrome-devtools://devtools/bundled/devtools.html?ws=localhost:9898"
|
||||
<< std::endl;
|
||||
Respond(connection_id,
|
||||
"Open the following URL in Chrome:\n"
|
||||
"chrome-devtools://devtools/bundled/devtools.html?ws=localhost:9898\n");
|
||||
}
|
||||
|
||||
void Quit() {
|
||||
void Quit(int connection_id) {
|
||||
std::cout << "quitting" << std::endl;
|
||||
debugger_->Shutdown();
|
||||
}
|
||||
|
||||
void ToggleTracing() {
|
||||
void ToggleTracing(int connection_id) {
|
||||
if (is_tracing_) {
|
||||
std::cout << "Stopping trace (writing to sky_viewer.trace)" << std::endl;
|
||||
tracing_->StopAndFlush();
|
||||
@ -151,6 +138,7 @@ class Prompt : public mojo::ApplicationDelegate {
|
||||
tracing_->Start(mojo::String("sky_viewer"), mojo::String("*"));
|
||||
}
|
||||
is_tracing_ = !is_tracing_;
|
||||
Respond(connection_id, "OK\n");
|
||||
}
|
||||
|
||||
bool is_tracing_;
|
||||
@ -158,6 +146,7 @@ class Prompt : public mojo::ApplicationDelegate {
|
||||
tracing::TraceCoordinatorPtr tracing_;
|
||||
std::string url_;
|
||||
base::WeakPtrFactory<Prompt> weak_ptr_factory_;
|
||||
scoped_ptr<net::HttpServer> web_server_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Prompt);
|
||||
};
|
||||
@ -167,5 +156,6 @@ class Prompt : public mojo::ApplicationDelegate {
|
||||
|
||||
MojoResult MojoMain(MojoHandle shell_handle) {
|
||||
mojo::ApplicationRunnerChromium runner(new sky::debugger::Prompt);
|
||||
runner.set_message_loop_type(base::MessageLoop::TYPE_IO);
|
||||
return runner.Run(shell_handle);
|
||||
}
|
||||
|
@ -6,8 +6,11 @@
|
||||
from skypy.paths import Paths
|
||||
from skypy.skyserver import SkyServer
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import requests
|
||||
import signal
|
||||
import skypy.configuration as configuration
|
||||
import subprocess
|
||||
import urlparse
|
||||
@ -19,13 +22,17 @@ SUPPORTED_MIME_TYPES = [
|
||||
'text/plain',
|
||||
]
|
||||
|
||||
|
||||
HTTP_PORT = 9999
|
||||
PID_FILE_PATH = "/tmp/skydb.pids"
|
||||
|
||||
|
||||
class SkyDebugger(object):
|
||||
def __init__(self):
|
||||
self.paths = None
|
||||
self.pids = {}
|
||||
# FIXME: This is not android aware nor aware of the port
|
||||
# skyserver is listening on.
|
||||
self.base_url = 'http://localhost:7777'
|
||||
|
||||
def _server_root_and_url_from_path_arg(self, url_or_path):
|
||||
# This is already a valid url we don't need a local server.
|
||||
@ -46,19 +53,8 @@ class SkyDebugger(object):
|
||||
def _in_chromoting(self):
|
||||
return os.environ.get('CHROME_REMOTE_DESKTOP_SESSION', False)
|
||||
|
||||
def main(self):
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
parser = argparse.ArgumentParser(description='Sky launcher/debugger')
|
||||
parser.add_argument('--gdb', action='store_true')
|
||||
parser.add_argument('--use-osmesa', action='store_true',
|
||||
default=self._in_chromoting())
|
||||
parser.add_argument('url_or_path', nargs='?', type=str)
|
||||
parser.add_argument('--show-command', action='store_true',
|
||||
help='Display the shell command and exit')
|
||||
configuration.add_arguments(parser)
|
||||
args = parser.parse_args()
|
||||
|
||||
def _build_mojo_shell_command(self, args):
|
||||
sky_server = None
|
||||
self.paths = Paths(os.path.join('out', args.configuration))
|
||||
|
||||
content_handlers = ['%s,%s' % (mime_type, 'mojo:sky_viewer')
|
||||
@ -73,33 +69,126 @@ class SkyDebugger(object):
|
||||
if args.use_osmesa:
|
||||
shell_command.append('--args-for=mojo:native_viewport_service --use-osmesa')
|
||||
|
||||
server_root = None
|
||||
|
||||
if args.url_or_path:
|
||||
# Check if we need a local server for the url/path arg:
|
||||
server_root, url = \
|
||||
self._server_root_and_url_from_path_arg(args.url_or_path)
|
||||
sky_server = SkyServer(self.paths, HTTP_PORT, args.configuration,
|
||||
server_root)
|
||||
|
||||
prompt_args = '--args-for=mojo:sky_debugger_prompt %s' % url
|
||||
shell_command.append(prompt_args)
|
||||
|
||||
if args.gdb:
|
||||
shell_command = ['gdb', '--args'] + shell_command
|
||||
|
||||
if server_root:
|
||||
with SkyServer(self.paths, HTTP_PORT, args.configuration,
|
||||
server_root):
|
||||
subprocess.check_call(shell_command)
|
||||
else:
|
||||
subprocess.check_call(shell_command)
|
||||
return shell_command, sky_server
|
||||
|
||||
def start_command(self, args):
|
||||
shell_command, sky_server = self._build_mojo_shell_command(args)
|
||||
if args.show_command:
|
||||
print " ".join(shell_command)
|
||||
else:
|
||||
subprocess.check_call(shell_command)
|
||||
return
|
||||
|
||||
def shutdown(self):
|
||||
print "Quitting"
|
||||
if self._sky_server:
|
||||
self._sky_server.terminate()
|
||||
self.stop_command([]) # Quit any existing process.
|
||||
|
||||
if sky_server:
|
||||
self.pids['sky_server_pid'] = sky_server.start()
|
||||
# self.pids['sky_server_port'] = sky_server.port
|
||||
# self.pids['sky_server_root'] = sky_server.root
|
||||
self.pids['mojo_shell_pid'] = subprocess.Popen(shell_command).pid
|
||||
|
||||
def _kill_if_exists(self, key, name):
|
||||
pid = self.pids.pop(key, None)
|
||||
if not pid:
|
||||
logging.info('No pid for %s, nothing to do.' % name)
|
||||
return
|
||||
logging.info('Killing %s (%s).' % (name, pid))
|
||||
try:
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
except OSError:
|
||||
logging.info('%s (%s) already gone.' % (name, pid))
|
||||
|
||||
def stop_command(self, args):
|
||||
# FIXME: Send /quit to sky prompt instead of killing.
|
||||
# self._send_command_to_sky('/quit')
|
||||
self._kill_if_exists('mojo_shell_pid', 'mojo_shell')
|
||||
self._kill_if_exists('sky_server_pid', 'sky_server')
|
||||
|
||||
def load_command(self, args):
|
||||
# Should resolve paths to relative urls like start does.
|
||||
# self.pids['sky_server_root'] and port should help.
|
||||
self._send_command_to_sky('/load', args.url_or_path)
|
||||
|
||||
def _send_command_to_sky(self, command_path, payload=None):
|
||||
url = self.base_url + command_path
|
||||
if payload:
|
||||
response = requests.post(url, payload)
|
||||
else:
|
||||
response = requests.get(url)
|
||||
print response.text
|
||||
|
||||
# FIXME: These could be made into a context object with __enter__/__exit__.
|
||||
def _load_pid_file(self, path):
|
||||
try:
|
||||
with open(path, 'r') as pid_file:
|
||||
return json.load(pid_file)
|
||||
except:
|
||||
if os.path.exists(path):
|
||||
logging.warn('Failed to read pid file: %s' % path)
|
||||
return {}
|
||||
|
||||
def _write_pid_file(self, path, pids):
|
||||
try:
|
||||
with open(path, 'w') as pid_file:
|
||||
json.dump(pids, pid_file)
|
||||
except:
|
||||
logging.warn('Failed to write pid file: %s' % path)
|
||||
|
||||
def _add_basic_command(self, subparsers, name, url_path, help_text):
|
||||
parser = subparsers.add_parser(name, help=help_text)
|
||||
command = lambda args: self._send_command_to_sky(url_path)
|
||||
parser.set_defaults(func=command)
|
||||
|
||||
def main(self):
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
self.pids = self._load_pid_file(PID_FILE_PATH)
|
||||
|
||||
parser = argparse.ArgumentParser(description='Sky launcher/debugger')
|
||||
subparsers = parser.add_subparsers(help='sub-command help')
|
||||
|
||||
start_parser = subparsers.add_parser('start',
|
||||
help='launch a new mojo_shell with sky')
|
||||
configuration.add_arguments(start_parser)
|
||||
start_parser.add_argument('--gdb', action='store_true')
|
||||
start_parser.add_argument('--use-osmesa', action='store_true',
|
||||
default=self._in_chromoting())
|
||||
start_parser.add_argument('url_or_path', nargs='?', type=str)
|
||||
start_parser.add_argument('--show-command', action='store_true',
|
||||
help='Display the shell command and exit')
|
||||
start_parser.set_defaults(func=self.start_command)
|
||||
|
||||
stop_parser = subparsers.add_parser('stop',
|
||||
help=('stop sky (as listed in %s)' % PID_FILE_PATH))
|
||||
stop_parser.set_defaults(func=self.stop_command)
|
||||
|
||||
self._add_basic_command(subparsers, 'trace', '/trace',
|
||||
'toggle tracing')
|
||||
self._add_basic_command(subparsers, 'reload', '/reload',
|
||||
'reload the current page')
|
||||
self._add_basic_command(subparsers, 'inspect', '/inspect',
|
||||
'stop the running sky instance')
|
||||
|
||||
load_parser = subparsers.add_parser('load',
|
||||
help='load a new page in the currently running sky')
|
||||
load_parser.add_argument('url_or_path', type=str)
|
||||
load_parser.set_defaults(func=self.load_command)
|
||||
|
||||
args = parser.parse_args()
|
||||
args.func(args)
|
||||
|
||||
self._write_pid_file(PID_FILE_PATH, self.pids)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -26,7 +26,7 @@ class SkyServer(object):
|
||||
'download_sky_server'))
|
||||
return os.path.join(paths.src_root, 'out', 'downloads', 'sky_server')
|
||||
|
||||
def __enter__(self):
|
||||
def start(self):
|
||||
if self._port_in_use(self.port):
|
||||
logging.warn(
|
||||
'Port %s already in use, assuming custom sky_server started.' %
|
||||
@ -41,11 +41,18 @@ class SkyServer(object):
|
||||
str(self.port),
|
||||
]
|
||||
self.server = subprocess.Popen(server_command)
|
||||
return self.server.pid
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
def stop(self):
|
||||
if self.server:
|
||||
self.server.terminate()
|
||||
|
||||
def __enter__(self):
|
||||
self.start()
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self.stop()
|
||||
|
||||
def path_as_url(self, path):
|
||||
return self.url_for_path(self.port, self.root, path)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user