File: //proc/self/root/usr/include/hphp/runtime/ext/xdebug/hook.h
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
| Copyright (c) 1997-2010 The PHP Group |
+----------------------------------------------------------------------+
| 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_XDEBUG_HOOK_H_
#define incl_HPHP_XDEBUG_HOOK_H_
#include "hphp/runtime/ext/xdebug/ext_xdebug.h"
#include "hphp/runtime/ext/xdebug/php5_xdebug/xdebug_var.h"
#include "hphp/runtime/ext/xdebug/server.h"
#include "hphp/runtime/base/req-ptr.h"
#include "hphp/runtime/vm/debugger-hook.h"
#include <boost/variant.hpp>
#include <folly/Optional.h>
namespace HPHP {
////////////////////////////////////////////////////////////////////////////////
/*
* This struct contains all the runtime information about a given breakpoint.
* Each breakpoint is tagged with its type.
*/
struct XDebugBreakpoint {
enum class Type : int8_t {
LINE,
CALL,
RETURN,
EXCEPTION
};
// The hit value will be compared against the hit count using one of these
// operators
enum class HitCondition : int8_t {
GREATER_OR_EQUAL,
EQUAL,
MULTIPLE
};
Type type; // The breakpoint type. This must be provided.
bool enabled = true; // Whether or not this breakpoint is enabled
bool resolved = false;
bool temporary = false; // If true, removed after being hit
HitCondition hitCondition = HitCondition::GREATER_OR_EQUAL;
int hitValue = 0; // Value to compare hitCount against
int hitCount = 0; // # of times this breakpoint has been hit
// A line breakpoint requires a filename and a line number. It has an
// optional condition represented by the given unit where the pseud-main is
// called when over the given file/line. It should returns true if we should
// break, false otherwise. Note that php5 xdebug doesn't distinguish
// internally between this and a conditional breakpoint (which is in the spec)
// The encoded condition is kept around for display purposes. The unit is
// updated to the matched unit once the breakpoint has been matched.
std::string fileName;
int line = -1;
Unit* conditionUnit = nullptr;
std::string condition;
const Unit* unit = nullptr;
// A call or return breakpoint occurs when the function in the given class
// with the given name is called/returns. The class name and function name
// are kept around for listing breakpoint info. The function id is added
// once the breakpoint has been matched with a function
folly::Optional<std::string> className;
std::string funcName;
std::string fullFuncName;
int funcId = -1;
// An exception breakpoint occurs when an exception with a given name is
// thrown.
std::string exceptionName;
};
////////////////////////////////////////////////////////////////////////////////
/* Thread local storage for breakpoints. */
struct XDebugThreadBreakpoints {
// Adding a breakpoint. Returns a unique id for the breakpoint. Throws an
// XDebugServer::ErrorCode on failure.
int addBreakpoint(XDebugBreakpoint& bp);
// Removing a breakpoint with the given id. If the id does not exit, does
// nothing
void removeBreakpoint(int id);
// Gets the breakpoint with the given id. Returns nullptr if the breakpoint
// does not exist
inline const XDebugBreakpoint* getBreakpoint(int id) {
auto iter = m_breakMap.find(id);
return iter != m_breakMap.end() ? &iter->second : nullptr;
}
// Breakpoint update methods. Some of these modify internal state, so it's
// better to use these to update breakpoints. There could be more update
// methods, but these are the only ones needed by the xdebug commmands right
// now. These return true on success, false on failure.
bool updateBreakpointLine(int id, int newLine);
bool updateBreakpointState(int id, bool enabled);
bool updateBreakpointHitCondition(int id, XDebugBreakpoint::HitCondition con);
bool updateBreakpointHitValue(int id, int hitValue);
// Map from id => breakpoint. This where breakpoints are stored
hphp_hash_map<int, XDebugBreakpoint> m_breakMap;
// Map from func id => breakpoint id for enter/exit breakpoints
hphp_hash_map<int, int> m_funcEntryMap;
hphp_hash_map<int, int> m_funcExitMap;
// Map of filename => map of line # => breakpoint ids for the line
// php5 xdebug allows multiple line breakpoints per line
hphp_string_map<std::multimap<int, int> > m_lineMap;
// Map from exception name => breakpoint id
hphp_string_map<int> m_exceptionMap;
// Set of breakpoint ids that don't yet have valid location. php5
// xdebug does no validity checking on breakpoint input and just assumes if
// function or file/line does not exist, it will in the future.
hphp_hash_set<int> m_unmatched;
// The next breakpoint id to assign
int m_nextBreakpointId = 0;
};
extern DECLARE_THREAD_LOCAL_NO_CHECK(XDebugThreadBreakpoints,
s_xdebug_breakpoints);
// Breakpoint entrypoints for other files
#define XDEBUG_ADD_BREAKPOINT(bp) \
(s_xdebug_breakpoints->addBreakpoint(bp));
#define XDEBUG_REMOVE_BREAKPOINT(id) \
(s_xdebug_breakpoints->removeBreakpoint(id))
#define XDEBUG_GET_BREAKPOINT(id) \
(s_xdebug_breakpoints->getBreakpoint(id))
#define XDEBUG_BREAKPOINTS \
(s_xdebug_breakpoints->m_breakMap)
////////////////////////////////////////////////////////////////////////////////
/* Function enter/exit breakpoint. */
struct FuncBreak {
const Func* func;
bool entry;
};
/* Line breakpoint. */
struct LineBreak {
const Unit* unit;
int line;
};
/* Exception breakpoint (for both errors and exceptions). */
struct ExnBreak {
const StringData* name;
const StringData* message;
};
using BreakInfo = boost::variant<FuncBreak, LineBreak, ExnBreak>;
////////////////////////////////////////////////////////////////////////////////
struct XDebugHook : DebuggerHook {
static DebuggerHook* GetInstance();
// Starting the server on request init
void onRequestInit() override {
if (XDebugServer::isNeeded()) {
XDebugServer::attach(XDebugServer::Mode::Req);
}
}
// Stopping the server on request shutdown
void onRequestShutdown() override {
if (XDebugServer::isAttached()) {
XDebugServer::detach();
}
}
void onOpcode(PC pc) override;
// Generic breakpoint handling routine. Most of the break handling code is
// the same across the different breakpoint types.
void onBreak(const BreakInfo&);
void onFuncEntryBreak(const Func* func) override {
onBreak(FuncBreak { func, true });
}
void onFuncExitBreak(const Func* func) override {
onBreak(FuncBreak { func, false });
}
void onLineBreak(const Unit* unit, int line) override {
onBreak(LineBreak { unit, line });
}
// Helper for errors and exceptions that potentially starts the debug server
// if needed
void onExceptionBreak(const StringData* name, const StringData* msg) {
// Potentially start the debug server if it hasn't been started.
if (XDEBUG_GLOBAL(RemoteEnable) &&
!XDebugServer::isAttached() &&
XDEBUG_GLOBAL(RemoteMode) == "jit") {
XDebugServer::attach(XDebugServer::Mode::Jit);
}
// Handle the exception.
onBreak(ExnBreak { name, msg });
}
void onExceptionThrown(ObjectData* exception) override;
void onError(const ExtendedException& ee,
int errnum,
const std::string& message) override {
auto const name = xdebug_error_type(errnum);
auto const msg = String(message);
onExceptionBreak(name.get(), msg.get());
}
// Flow control. Each break type just calls the generic onFlowBreak.
void onFlowBreak(const Unit* unit, int line);
void onStepInBreak(const Unit* unit, int line) override {
onFlowBreak(unit, line);
}
void onStepOutBreak(const Unit* unit, int line) override {
onFlowBreak(unit, line);
}
void onNextBreak(const Unit* unit, int line) override {
onFlowBreak(unit, line);
}
// Handle loading unmatched breakpoints.
void onFileLoad(Unit* efile) override;
void onDefClass(const Class* cls) override;
void onDefFunc(const Func* func) override;
private:
explicit XDebugHook() {}
~XDebugHook() {}
};
///////////////////////////////////////////////////////////////////////////////
}
#endif