f3d5a8abb1
A client couldn't break execution during eval. There used to be a lot of barriers to making that right, but I fixed most of them with a previous diff on unifying client-side event loops. Now the only barrier was that a server-side thread processing an interrupt was blocking the signal polling thread by holding a mutex while processing the interrupt. Changed to set a flag to disable polling when starting to process the interrupt (and unsetting it when done), while still synchronizing with the signal polling thread to ensure only one thread is sending the client messages at a time. Added logic to re-enable polling while executing PHP for eval, print, etc. Plumbed the proxy thru to the point where we check the clause on conditional breakpoints, too, since that's the third (and final) place we do this.
159 linhas
5.4 KiB
C++
159 linhas
5.4 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010-2013 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. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#ifndef incl_HPHP_EVAL_DEBUGGER_PROXY_H_
|
|
#define incl_HPHP_EVAL_DEBUGGER_PROXY_H_
|
|
|
|
#include "hphp/util/base.h"
|
|
#include "hphp/util/synchronizable.h"
|
|
#include "hphp/util/async_func.h"
|
|
#include "hphp/runtime/base/socket.h"
|
|
#include "hphp/runtime/debugger/dummy_sandbox.h"
|
|
|
|
namespace HPHP { namespace Eval {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// A DebuggerProxy provides a conection thru which a client may talk to a VM
|
|
// which is being debugged. The VM can also send messages to the client via the
|
|
// proxy, either in reponse to messages from the client, or to poll the client
|
|
// for information.
|
|
//
|
|
// In an basic scenario where a client is debugging a remote VM, the VM will
|
|
// create a proxy when the client connects (via DebuggerServer) and listen for
|
|
// commands via this proxy. It will use this proxy when completing control flow
|
|
// commands to interrupt the client. The client sends and receives messages over
|
|
// a socket directly to this proxy. Thus we have:
|
|
//
|
|
// Client <---> Proxy <---> VM
|
|
//
|
|
// The client always creates its own "local proxy", which allows debugging any
|
|
// code running on the VM within the client. The two are easily confused.
|
|
//
|
|
|
|
class CmdInterrupt;
|
|
DECLARE_BOOST_TYPES(DebuggerProxy);
|
|
DECLARE_BOOST_TYPES(DebuggerCommand);
|
|
DECLARE_BOOST_TYPES(CmdFlowControl);
|
|
|
|
class DebuggerProxy : public Synchronizable,
|
|
public boost::enable_shared_from_this<DebuggerProxy> {
|
|
public:
|
|
enum ThreadMode {
|
|
Normal,
|
|
Sticky,
|
|
Exclusive
|
|
};
|
|
|
|
static std::string MakePHP(const std::string &php);
|
|
static std::string MakePHPReturn(const std::string &php);
|
|
|
|
public:
|
|
DebuggerProxy(SmartPtr<Socket> socket, bool local);
|
|
~DebuggerProxy();
|
|
|
|
bool isLocal() const { return m_local;}
|
|
|
|
const char *getThreadType() const;
|
|
DSandboxInfo getSandbox() const;
|
|
std::string getSandboxId() const;
|
|
const DSandboxInfo& getDummyInfo() const { return m_dummyInfo; }
|
|
|
|
void getThreads(DThreadInfoPtrVec &threads);
|
|
bool switchSandbox(const std::string &newId, bool force);
|
|
void updateSandbox(DSandboxInfoPtr sandbox);
|
|
bool switchThread(DThreadInfoPtr thread);
|
|
void switchThreadMode(ThreadMode mode, int64_t threadId = 0);
|
|
|
|
void startDummySandbox();
|
|
void notifyDummySandbox();
|
|
|
|
void setBreakPoints(BreakPointInfoPtrVec &breakpoints);
|
|
void getBreakPoints(BreakPointInfoPtrVec &breakpoints);
|
|
BreakPointInfoPtr getBreakPointAtCmd(CmdInterrupt& cmd);
|
|
|
|
bool needInterrupt();
|
|
bool needVMInterrupts();
|
|
void interrupt(CmdInterrupt &cmd);
|
|
bool sendToClient(DebuggerCommand *cmd);
|
|
CmdInterrupt& currentInterruptCmd();
|
|
|
|
int getStackDepth();
|
|
int getRealStackDepth();
|
|
|
|
void startSignalThread();
|
|
void pollSignal(); // for signal polling thread
|
|
|
|
void checkStop();
|
|
void forceQuit();
|
|
void stop();
|
|
|
|
bool getClientConnectionInfo(VRefParam address, VRefParam port);
|
|
|
|
enum ExecutePHPFlags {
|
|
ExecutePHPFlagsNone = 0x0, // No logging, not at an interrupt
|
|
ExecutePHPFlagsLog = 0x1, // Add logs to the output string
|
|
ExecutePHPFlagsAtInterrupt = 0x02 // Called when stopped at an interrupt
|
|
};
|
|
|
|
Variant ExecutePHP(const std::string &php, String &output, int frame,
|
|
int flags);
|
|
|
|
private:
|
|
bool blockUntilOwn(CmdInterrupt &cmd, bool check);
|
|
bool checkBreakPoints(CmdInterrupt &cmd);
|
|
bool checkFlowBreak(CmdInterrupt &cmd);
|
|
void processInterrupt(CmdInterrupt &cmd);
|
|
void enableSignalPolling();
|
|
void disableSignalPolling();
|
|
|
|
DThreadInfoPtr createThreadInfo(const std::string &desc);
|
|
|
|
void changeBreakPointDepth(CmdInterrupt& cmd);
|
|
|
|
SmartPtr<Socket> getSocket() { return m_thrift.getSocket(); }
|
|
|
|
bool m_stopped;
|
|
|
|
bool m_local;
|
|
DebuggerThriftBuffer m_thrift;
|
|
DummySandbox* m_dummySandbox;
|
|
|
|
mutable Mutex m_mutex;
|
|
ReadWriteMutex m_breakMutex;
|
|
bool m_hasBreakPoints;
|
|
BreakPointInfoPtrVec m_breakpoints;
|
|
DSandboxInfo m_sandbox;
|
|
DSandboxInfo m_dummyInfo;
|
|
|
|
ThreadMode m_threadMode;
|
|
int64_t m_thread;
|
|
DThreadInfoPtr m_newThread;
|
|
std::map<int64_t, DThreadInfoPtr> m_threads;
|
|
|
|
CmdFlowControlPtr m_flow; // c, s, n, o commands that can skip breakpoints
|
|
|
|
Mutex m_signalMutex; // who can talk to client
|
|
AsyncFunc<DebuggerProxy> m_signalThread; // polling signals from client
|
|
bool m_okayToPoll; // whether the polling thread can send polls to the client
|
|
|
|
Mutex m_signumMutex;
|
|
int m_signum;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}}
|
|
|
|
#endif // incl_HPHP_EVAL_DEBUGGER_PROXY_H_
|