HEX
Server: Apache
System: Linux wp02.tdr-lab.com 3.10.0-1160.42.2.el7.x86_64 #1 SMP Tue Sep 7 14:49:57 UTC 2021 x86_64
User: kusanagi (1001)
PHP: 7.4.23
Disabled: NONE
Upload Files
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