98466ea3fd
Converted lots of callsites to StaticString, removed a few dead overloaded litstr functions.
317 linhas
11 KiB
C++
317 linhas
11 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include <runtime/eval/debugger/cmd/cmd_machine.h>
|
|
#include <runtime/eval/debugger/cmd/cmd_signal.h>
|
|
#include <runtime/base/runtime_option.h>
|
|
#include <runtime/base/intercept.h>
|
|
#include <runtime/base/array/array_init.h>
|
|
#include <runtime/base/util/libevent_http_client.h>
|
|
#include <util/process.h>
|
|
|
|
namespace HPHP { namespace Eval {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CmdMachine::sendImpl(DebuggerThriftBuffer &thrift) {
|
|
DebuggerCommand::sendImpl(thrift);
|
|
thrift.write(m_sandboxes);
|
|
thrift.write(m_rpcConfig);
|
|
thrift.write(m_force);
|
|
thrift.write(m_succeed);
|
|
}
|
|
|
|
void CmdMachine::recvImpl(DebuggerThriftBuffer &thrift) {
|
|
DebuggerCommand::recvImpl(thrift);
|
|
thrift.read(m_sandboxes);
|
|
thrift.read(m_rpcConfig);
|
|
thrift.read(m_force);
|
|
thrift.read(m_succeed);
|
|
}
|
|
|
|
void CmdMachine::list(DebuggerClient *client) {
|
|
if (client->argCount() == 0) {
|
|
static const char *keywords[] =
|
|
{ "disconnect", "connect", "rpc", "list", "attach", nullptr };
|
|
client->addCompletion(keywords);
|
|
}
|
|
}
|
|
|
|
bool CmdMachine::help(DebuggerClient *client) {
|
|
client->helpTitle("Machine Command");
|
|
client->helpCmds(
|
|
"[m]achine [c]onnect {host}", "debugging remote server natively",
|
|
"[m]achine [c]onnect {host}:{port}", "debugging remote server natively",
|
|
"[m]achine [r]pc {host}", "debugging remote server with RPC",
|
|
"[m]achine [r]pc {host}:{port}", "debugging remote server with RPC",
|
|
"[m]achine [d]isconnect", "debugging local script",
|
|
"[m]achine [l]ist", "list all sandboxes",
|
|
"[m]achine [a]ttach {index}", "attach to a sandbox",
|
|
"[m]achine [a]ttach {sandbox}", "attach to my sandbox by name",
|
|
"[m]achine [a]ttach {user} {sandbox}",
|
|
"attach to a sandbox by user and name",
|
|
nullptr
|
|
);
|
|
client->helpBody(
|
|
"Use this command to switch between different machines or "
|
|
"sandboxes.\n"
|
|
"\n"
|
|
"If command prompt says \"hphpd\", all evaluation of PHP code happens "
|
|
"locally within the debugger. This is the mode when debugger is started "
|
|
"without a remote server name. No user libraries are pre-loaded in this "
|
|
"mode.\n"
|
|
"\n"
|
|
"When connecting to a remote server, it will automatically attach "
|
|
"to \"default\" sandbox under current user. If \"default\" sandbox "
|
|
"does not exist, it will attach to a random sandbox under current "
|
|
"user. In sandbox mode, a file specified in server's configuration "
|
|
"of \"Eval.Debugger.StartupDocument\" is pre-loaded.\n"
|
|
"\n"
|
|
"If there is no sandbox available, it will create a \"dummy\" "
|
|
"sandbox and attach to it.\n"
|
|
"\n"
|
|
"When your sandbox is not available, please hit it at least once "
|
|
"from your browser. Then run '[m]achine [l]ist' command again.\n"
|
|
"\n"
|
|
"If a HipHop server has RPC port open, one can also debug the server in "
|
|
"a very special RPC mode. In this mode, one can type in PHP scripts to "
|
|
"run, but all functions will be executed on server through RPC. Because "
|
|
"states are still maintained locally and only functions are executed "
|
|
"remotely, it may not work with functions or scripts that depend on "
|
|
"global variables or low-level raw resource pointers. As a simple rule, "
|
|
"stateless functions will work just fine. This is true to objects and "
|
|
"method calls as well, except classes will have to be loaded on client "
|
|
"side by '=include(\"file-containing-the-class.php\")'."
|
|
);
|
|
return true;
|
|
}
|
|
|
|
bool CmdMachine::processList(DebuggerClient *client,
|
|
bool output /* = true */) {
|
|
m_body = "list";
|
|
CmdMachinePtr res = client->xend<CmdMachine>(this);
|
|
client->updateSandboxes(res->m_sandboxes);
|
|
if (!output) return true;
|
|
|
|
if (res->m_sandboxes.empty()) {
|
|
client->info("(no sandbox was found)");
|
|
client->tutorial(
|
|
"Please hit the sandbox from browser at least once. Then run "
|
|
"'[m]achine [l]ist' again."
|
|
);
|
|
} else {
|
|
for (int i = 0; i < (int)res->m_sandboxes.size(); i++) {
|
|
client->print(" %d\t%s", i + 1,
|
|
res->m_sandboxes[i]->desc().c_str());
|
|
}
|
|
client->tutorial(
|
|
"Use '[m]achine [a]ttach {index}' to attach to one sandbox. For "
|
|
"example, 'm a 1'. If desired sandbox is not on the list, please "
|
|
"hit the sandbox from browser once. Then run '[m]achine [l]ist' "
|
|
"again."
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CmdMachine::AttachSandbox(DebuggerClient *client,
|
|
const char *user /* = NULL */,
|
|
const char *name /* = NULL */,
|
|
bool force /* = false */) {
|
|
string login;
|
|
if (user == nullptr) {
|
|
login = client->getCurrentUser();
|
|
user = login.c_str();
|
|
}
|
|
if (client->isApiMode()) {
|
|
force = true;
|
|
}
|
|
|
|
DSandboxInfoPtr sandbox(new DSandboxInfo());
|
|
sandbox->m_user = user ? user : "";
|
|
sandbox->m_name = (name && *name) ? name : "default";
|
|
return AttachSandbox(client, sandbox, force);
|
|
}
|
|
|
|
bool CmdMachine::AttachSandbox(DebuggerClient *client,
|
|
DSandboxInfoPtr sandbox,
|
|
bool force /* = false */) {
|
|
if (client->isLocal()) {
|
|
client->error("Local script doesn't have sandbox to attach to.");
|
|
return false;
|
|
}
|
|
|
|
CmdMachine cmd;
|
|
cmd.m_body = "attach";
|
|
cmd.m_sandboxes.push_back(sandbox);
|
|
cmd.m_force = force;
|
|
|
|
client->info("Attaching to %s and pre-loading, please wait...",
|
|
sandbox->desc().c_str());
|
|
CmdMachinePtr cmdMachine = client->xend<CmdMachine>(&cmd);
|
|
if (cmdMachine->m_succeed) {
|
|
client->playMacro("startup");
|
|
} else {
|
|
client->error("failed to attach to sandbox, maybe another client is "
|
|
"debugging, \nattach to another sandbox, exit the "
|
|
"attached hphpd client, or try \n"
|
|
"[m]achine [a]ttach [f]orce [%s] [%s]",
|
|
sandbox->m_user.c_str(), sandbox->m_name.c_str());
|
|
}
|
|
return cmdMachine->m_succeed;
|
|
}
|
|
|
|
static const StaticString s_host("host");
|
|
static const StaticString s_port("port");
|
|
static const StaticString s_auth("auth");
|
|
static const StaticString s_timeout("timeout");
|
|
|
|
void CmdMachine::UpdateIntercept(DebuggerClient *client,
|
|
const std::string &host, int port) {
|
|
CmdMachine cmd;
|
|
cmd.m_body = "rpc";
|
|
cmd.m_rpcConfig = CREATE_MAP4
|
|
(s_host, String(host),
|
|
s_port, port ? port : RuntimeOption::DebuggerDefaultRpcPort,
|
|
s_auth, String(RuntimeOption::DebuggerDefaultRpcAuth),
|
|
s_timeout, RuntimeOption::DebuggerDefaultRpcTimeout);
|
|
client->xend<CmdMachine>(&cmd);
|
|
}
|
|
|
|
bool CmdMachine::onClient(DebuggerClient *client) {
|
|
if (DebuggerCommand::onClient(client)) return true;
|
|
if (client->argCount() == 0) return help(client);
|
|
|
|
bool rpc = client->arg(1, "rpc");
|
|
if (rpc || client->arg(1, "connect")) {
|
|
if (client->argCount() != 2) {
|
|
return help(client);
|
|
}
|
|
string host = client->argValue(2);
|
|
int port = 0;
|
|
size_t pos = host.find(":");
|
|
if (pos != string::npos) {
|
|
if (!DebuggerClient::IsValidNumber(host.substr(pos + 1))) {
|
|
client->error("Port needs to be a number");
|
|
return help(client);
|
|
}
|
|
port = atoi(host.substr(pos + 1).c_str());
|
|
host = host.substr(0, pos);
|
|
}
|
|
|
|
if (rpc) {
|
|
if (client->connectRPC(host, port)) {
|
|
throw DebuggerConsoleExitException();
|
|
}
|
|
} else {
|
|
if (client->connect(host, port)) {
|
|
throw DebuggerConsoleExitException();
|
|
}
|
|
}
|
|
client->initializeMachine();
|
|
return true;
|
|
}
|
|
|
|
if (client->arg(1, "disconnect")) {
|
|
if (client->disconnect()) {
|
|
throw DebuggerConsoleExitException();
|
|
}
|
|
client->initializeMachine();
|
|
return true;
|
|
}
|
|
|
|
if (client->arg(1, "list")) {
|
|
processList(client);
|
|
return true;
|
|
}
|
|
|
|
if (client->arg(1, "attach")) {
|
|
DSandboxInfoPtr sandbox;
|
|
|
|
string snum = client->argValue(2);
|
|
if (DebuggerClient::IsValidNumber(snum)) {
|
|
int num = atoi(snum.c_str());
|
|
sandbox = client->getSandbox(num);
|
|
if (!sandbox) {
|
|
processList(client, false);
|
|
sandbox = client->getSandbox(num);
|
|
if (!sandbox) {
|
|
client->error("\"%s\" is not a valid sandbox index. Choose one from "
|
|
"this list:", snum.c_str());
|
|
processList(client);
|
|
return true;
|
|
}
|
|
}
|
|
} else {
|
|
int argBase = 2;
|
|
if (client->argCount() >= 2 && client->arg(2, "force")) {
|
|
m_force = true;
|
|
argBase++;
|
|
}
|
|
sandbox = DSandboxInfoPtr(new DSandboxInfo());
|
|
if (client->argCount() < argBase) {
|
|
sandbox->m_user = client->getCurrentUser();
|
|
sandbox->m_name = "default";
|
|
} else if (client->argCount() == argBase) {
|
|
sandbox->m_user = client->getCurrentUser();
|
|
sandbox->m_name = client->argValue(argBase);
|
|
} else if (client->argCount() == argBase + 1) {
|
|
sandbox->m_user = client->argValue(argBase);
|
|
sandbox->m_name = client->argValue(argBase + 1);
|
|
} else {
|
|
return help(client);
|
|
}
|
|
}
|
|
if (AttachSandbox(client, sandbox, m_force)) {
|
|
// Attach succeed, wait for next interrupt
|
|
throw DebuggerConsoleExitException();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return help(client);
|
|
}
|
|
|
|
bool CmdMachine::onServer(DebuggerProxy *proxy) {
|
|
if (m_body == "rpc") {
|
|
String host = m_rpcConfig["host"].toString();
|
|
if (host.empty()) {
|
|
register_intercept("", false, uninit_null());
|
|
} else {
|
|
int port = m_rpcConfig["port"].toInt32();
|
|
LibEventHttpClient::SetCache(host.data(), port, 1);
|
|
register_intercept("", "fb_rpc_intercept_handler", m_rpcConfig);
|
|
}
|
|
return proxy->send(this);
|
|
}
|
|
if (m_body == "list") {
|
|
Debugger::GetRegisteredSandboxes(m_sandboxes);
|
|
return proxy->send(this);
|
|
}
|
|
if (m_body == "attach" && !m_sandboxes.empty()) {
|
|
m_succeed = proxy->switchSandbox(m_sandboxes[0]->id(), m_force);
|
|
if (m_succeed) {
|
|
proxy->notifyDummySandbox();
|
|
m_exitInterrupt = true;
|
|
}
|
|
return proxy->send(this);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}}
|