1bf6c5d588
When debugging a script using a local VM (i.e. when there is no remote VM), the cleanup actions performed by the run command caused the debugger client to shut down as well. Since this seems to be intentional, processing of the run command now restarts the debugger client and proxy. Also, following a ctrl-C, the m_lastLocFilter field must be cleared, otherwise an empty endless loop cannot be broken into for a second time.
215 linhas
6.8 KiB
C++
215 linhas
6.8 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 <boost/noncopyable.hpp>
|
|
|
|
#include <runtime/eval/debugger/dummy_sandbox.h>
|
|
#include <runtime/eval/debugger/debugger.h>
|
|
#include <runtime/eval/debugger/cmd/cmd_signal.h>
|
|
#include <runtime/base/program_functions.h>
|
|
#include <runtime/base/server/source_root_info.h>
|
|
#include <runtime/base/externals.h>
|
|
#include <runtime/base/hphp_system.h>
|
|
#include <util/logger.h>
|
|
#include <util/process.h>
|
|
|
|
namespace HPHP { namespace Eval {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
static const Trace::Module TRACEMOD = Trace::debugger;
|
|
|
|
DummySandbox::DummySandbox(DebuggerProxy *proxy,
|
|
const std::string &defaultPath,
|
|
const std::string &startupFile)
|
|
: m_proxy(proxy), m_defaultPath(defaultPath), m_startupFile(startupFile),
|
|
m_stopped(false),
|
|
m_signum(CmdSignal::SignalNone) {
|
|
TRACE(2, "DummySandbox::DummySandbox\n");
|
|
m_thread = new AsyncFunc<DummySandbox>(this, &DummySandbox::run);
|
|
}
|
|
|
|
bool DummySandbox::waitForEnd(int seconds) {
|
|
TRACE(2, "DummySandbox::waitForEnd\n");
|
|
bool ret = m_thread->waitForEnd(seconds);
|
|
if (ret) {
|
|
delete m_thread;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void DummySandbox::start() {
|
|
TRACE(2, "DummySandbox::start\n");
|
|
m_thread->start();
|
|
}
|
|
|
|
void DummySandbox::stop() {
|
|
TRACE(2, "DummySandbox::stop\n");
|
|
m_stopped = true;
|
|
ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
|
|
if (ti->m_reqInjectionData.dummySandbox) {
|
|
// called from dummy sandbox thread itself, schedule retirement
|
|
Debugger::RetireDummySandboxThread(this);
|
|
} else {
|
|
// called from worker thread, we wait for the dummySandbox to end
|
|
m_thread->waitForEnd();
|
|
// we are sure it's always created by new and this is the last thing
|
|
// on this object
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct CLISession : boost::noncopyable {
|
|
CLISession() {
|
|
TRACE(2, "CLISession::CLISession\n");
|
|
char *argv[] = {"", nullptr};
|
|
execute_command_line_begin(1, argv, 0);
|
|
}
|
|
~CLISession() {
|
|
TRACE(2, "CLISession::~CLISession\n");
|
|
Debugger::UnregisterSandbox(g_context->getSandboxId());
|
|
ThreadInfo::s_threadInfo.getNoCheck()->
|
|
m_reqInjectionData.debugger = false;
|
|
execute_command_line_end(0, false, nullptr);
|
|
Eval::DebuggerClient::Shutdown();
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
void DummySandbox::run() {
|
|
TRACE(2, "DummySandbox::run\n");
|
|
ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
|
|
Debugger::RegisterThread();
|
|
ti->m_reqInjectionData.dummySandbox = true;
|
|
while (!m_stopped) {
|
|
try {
|
|
CLISession hphpSession;
|
|
|
|
DSandboxInfo sandbox = m_proxy->getSandbox();
|
|
string msg;
|
|
if (sandbox.valid()) {
|
|
SystemGlobals *g = (SystemGlobals *)get_global_variables();
|
|
SourceRootInfo sri(sandbox.m_user, sandbox.m_name);
|
|
if (sandbox.m_path.empty()) {
|
|
sandbox.m_path = sri.path();
|
|
}
|
|
if (!sri.sandboxOn()) {
|
|
msg = "Invalid sandbox was specified. "
|
|
"PHP files may not be loaded properly.\n";
|
|
} else {
|
|
sri.setServerVariables(g->GV(_SERVER));
|
|
}
|
|
Debugger::RegisterSandbox(sandbox);
|
|
g_context->setSandboxId(sandbox.id());
|
|
|
|
char cwd[PATH_MAX];
|
|
getcwd(cwd, sizeof(cwd));
|
|
std::string doc = getStartupDoc(sandbox);
|
|
Logger::Info("Start loading startup doc '%s', pwd = '%s'",
|
|
doc.c_str(), cwd);
|
|
bool error; string errorMsg;
|
|
bool ret = hphp_invoke(g_context.getNoCheck(), doc, false, null_array,
|
|
uninit_null(), "", "", error, errorMsg, true, false,
|
|
true);
|
|
if (!ret || error) {
|
|
msg += "Unable to pre-load " + doc;
|
|
if (!errorMsg.empty()) {
|
|
msg += ": " + errorMsg;
|
|
}
|
|
}
|
|
Logger::Info("Startup doc " + doc + " loaded");
|
|
} else {
|
|
g_context->setSandboxId(m_proxy->getDummyInfo().id());
|
|
}
|
|
|
|
ti->m_reqInjectionData.debugger = true;
|
|
{
|
|
DebuggerDummyEnv dde;
|
|
Debugger::InterruptSessionStarted(nullptr, msg.c_str());
|
|
}
|
|
|
|
// Blocking until Ctrl-C is issued by end user and DebuggerProxy cannot
|
|
// find a real sandbox thread to handle it.
|
|
{
|
|
Lock lock(this);
|
|
while (!m_stopped && m_signum != CmdSignal::SignalBreak) {
|
|
wait(1);
|
|
}
|
|
if (m_stopped) {
|
|
// stopped by worker thread
|
|
break;
|
|
}
|
|
m_signum = CmdSignal::SignalNone;
|
|
}
|
|
} catch (const DebuggerClientExitException &e) {
|
|
// stopped by the dummy sandbox thread itself
|
|
break;
|
|
} catch (const DebuggerException &e) {
|
|
}
|
|
}
|
|
}
|
|
|
|
void DummySandbox::notifySignal(int signum) {
|
|
TRACE(2, "DummySandbox::notifySignal\n");
|
|
Lock lock(this);
|
|
m_signum = signum;
|
|
notify();
|
|
}
|
|
|
|
std::string DummySandbox::getStartupDoc(const DSandboxInfo &sandbox) {
|
|
TRACE(2, "DummySandbox::getStartupDoc\n");
|
|
string path;
|
|
if (!m_startupFile.empty()) {
|
|
// if relative path, prepend directory
|
|
if (m_startupFile[0] != '/' && m_startupFile[0] != '~') {
|
|
path = sandbox.m_path;
|
|
if (path.empty()) {
|
|
path = m_defaultPath;
|
|
}
|
|
}
|
|
if (!path.empty() && path[path.size() - 1] != '/') {
|
|
path += '/';
|
|
}
|
|
path += m_startupFile;
|
|
|
|
// resolving home directory
|
|
if (path[0] == '~') {
|
|
string user, home;
|
|
size_t pos = path.find('/');
|
|
if (pos == string::npos) pos = path.size();
|
|
if (pos > 1) {
|
|
user = path.substr(1, pos - 1);
|
|
}
|
|
if (user.empty()) user = sandbox.m_user;
|
|
if (user.empty() || user == Process::GetCurrentUser()) {
|
|
home = Process::GetHomeDirectory();
|
|
} else {
|
|
home = "/home/" + user + "/";
|
|
}
|
|
if (pos + 1 < path.size()) {
|
|
path = home + path.substr(pos + 1);
|
|
} else {
|
|
path = home;
|
|
}
|
|
}
|
|
}
|
|
return path;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}}
|