Arquivos
hhvm/hphp/runtime/debugger/cmd/cmd_flow_control.h
T
Mike Magruder 4048565e87 Ensure flow control commands don't remove internal breakpoints set by real breakpoints
A real breakpoint makes entries in the breakpoint filter for all offsets at the given line. Various flow control commands will also use the breakpoint filter to add and remove temporary "internal breakpoints" required during the flow operation. Ensure that we never remove a breakpoint filter entry if there was already one there due to a breakpoint (or really  any other reason).
2013-07-22 11:34:05 -07:00

136 linhas
5.5 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_CMD_FLOW_CONTROL_H_
#define incl_HPHP_EVAL_DEBUGGER_CMD_FLOW_CONTROL_H_
#include "hphp/runtime/debugger/debugger_command.h"
#include "hphp/runtime/debugger/cmd/cmd_interrupt.h"
namespace HPHP { namespace Eval {
///////////////////////////////////////////////////////////////////////////////
// CmdFlowControl is the base class of all "flow control" cmds: Continue, Step,
// Next, and Out. A DebuggerProxy can execute exactly one of these cmds at a
// time.
//
// 1. Proxy receives a flow command (say, Next).
// 2. Proxy calls the cmd's onSetup() with current source position.
// 3. Cmd sets up state (see below) in the VM to control stepping, etc.
// 4. If the cmd is complete the proxy deletes the cmd, otherwise it saves it
// in m_flow.
// 5. Proxy lets the thread continue execution.
// 6. Proxy receives an interrupt from the VM, delivers it to the cmd in m_flow.
// 7. Cmd inspects the site and VM state, and either decides it is done, or
// sets up state in the VM to continue the control flow operation.
// 8. If the cmd is complete the proxy stops and waits for more input from the
// client. Otherwise, rinse and repeat at step 5.
//
// When a cmd is setup, or receives an interrupt and determines it is not
// complete, it will do one of three things to ensure it gets interrupted again
// in the future:
//
// 1. Set m_needsVMInterrupt to true, to ensure we interpret and interrupt on
// each opcode executed.
// 2. Set m_needsVMInterrupt to true, and setup a location filter. This ensures
// we interpret, but only interrupt when the PC misses the location filter.
// 3. Place an "internal breakpoint", which ensures an interrupt when the
// breakpoint is hit without forcing us to interpret everything.
//
// The cmd may get interrupted for other reasons, such as an exception, reaching
// a breakpoint, a hard break, etc. All flow cmds are designed to tollerate this
// and remember enough state to determine if they should really transition their
// state or not.
DECLARE_BOOST_TYPES(CmdFlowControl);
class CmdFlowControl : public DebuggerCommand {
public:
explicit CmdFlowControl(Type type)
: DebuggerCommand(type), m_complete(false), m_needsVMInterrupt(false),
m_stackDepth(0), m_vmDepth(0), m_count(1) { }
virtual ~CmdFlowControl();
virtual bool onServer(DebuggerProxy &proxy);
// Work done to setup a new flow command, after receiving it from the client.
virtual void onSetup(DebuggerProxy &proxy, CmdInterrupt &interrupt) = 0;
// Work done when a VM thread interrupts the proxy.
virtual void onBeginInterrupt(DebuggerProxy &proxy,
CmdInterrupt &interrupt) = 0;
// A completed flow cmd has done all its work and can be deleted.
bool complete() { return m_complete; }
// Does this cmd need to force interrupts in the interpreter loop?
bool needsVMInterrupt() { return m_needsVMInterrupt; }
protected:
virtual void onClientImpl(DebuggerClient &client);
virtual void sendImpl(DebuggerThriftBuffer &thrift);
virtual void recvImpl(DebuggerThriftBuffer &thrift);
int decCount() { assert(m_count > 0); return --m_count;}
int getCount() const { assert(m_count > 0); return m_count;}
void installLocationFilterForLine(InterruptSite *site);
void removeLocationFilter();
void setupStepOuts();
void cleanupStepOuts();
bool hasStepOuts();
bool atStepOutOffset(Unit* unit, Offset o);
bool m_complete;
bool m_needsVMInterrupt;
// Support for stepping operations
int m_stackDepth;
int m_vmDepth;
std::string m_loc; // last break's source location
// Represents the destination of an internal stepping operation by
// unit and offset. Implictly maintains the breakpoint filter.
class StepDestination {
public:
StepDestination();
StepDestination(const HPHP::Unit* unit, Offset offset);
StepDestination(const StepDestination& other) = delete;
StepDestination& operator=(const StepDestination& other) = delete;
StepDestination(StepDestination&& other);
StepDestination& operator=(StepDestination&& other);
~StepDestination();
bool valid() const { return m_unit != nullptr; }
bool at(const Unit* unit, Offset o) const {
return (m_unit == unit) && (m_offset == o);
}
private:
const HPHP::Unit* m_unit;
Offset m_offset;
bool m_ownsInternalBreakpoint;
};
private:
StepDestination m_stepOut1;
StepDestination m_stepOut2;
int16_t m_count;
bool m_smallStep;
};
///////////////////////////////////////////////////////////////////////////////
}}
#endif // incl_HPHP_EVAL_DEBUGGER_CMD_FLOW_CONTROL_H_