// Helper that adds the given function breakpoint corresponding to the given
// function and id as a breakpoint. If a duplicate breakpoint already exists,
// it is overwritten.
static void add_func_breakpoint(int id,
                                XDebugBreakpoint& bp,
                                const Func* func) {
  // Function id is added to the breakpoint once matched
  auto const func_id = func->getFuncId();
  bp.funcId = func_id;

  // Add the appropriate function map
  switch (bp.type) {
    case BreakType::CALL: {
      auto iter = FUNC_ENTRY.find(func_id);
      if (iter != FUNC_ENTRY.end()) {
        XDEBUG_REMOVE_BREAKPOINT(iter->second);
      }
      FUNC_ENTRY[func->getFuncId()] = id;
      phpAddBreakPointFuncEntry(func);
      break;
    }
    case BreakType::RETURN: {
      auto iter = FUNC_EXIT.find(func_id);
      if (iter != FUNC_EXIT.end()) {
        XDEBUG_REMOVE_BREAKPOINT(iter->second);
      }
      FUNC_EXIT[func->getFuncId()] = id;
      phpAddBreakPointFuncExit(func);
      break;
    }
    default:
      throw Exception("Not passed a function breakpoint");
  }
}
Beispiel #2
0
void XDebugHook::onBreak(const BreakInfo& bi) {
  // Have to have a server to break.
  if (XDEBUG_GLOBAL(Server) == nullptr) {
    return;
  }

  // Grab the breakpoints matching the passed info
  std::vector<int> ids;
  get_breakpoint_ids(bi, ids);

  // Iterate.  Note that we only tell the server to break once.
  bool have_broken = false;
  for (auto const id : ids) {
    // Look up the breakpoint, ensure it's hittable.
    auto& bp = BREAKPOINT_MAP.at(id);
    if (!is_breakpoint_hit(bp)) {
      continue;
    }

    // We only break once per location.
    auto const temporary = bp.temporary; // breakpoint could be deleted
    if (!have_broken) {
      have_broken = true;

      // Grab the breakpoint message and do the break.
      auto const msg = get_breakpoint_message(bi);
      if (!XDEBUG_GLOBAL(Server)->breakpoint(bp, msg)) {
        // Kill the server if there's an error.
        XDebugServer::detach();
        return;
      }
    }

    // Remove the breakpoint if it was temporary.
    if (temporary) {
      XDEBUG_REMOVE_BREAKPOINT(id);
    }
  }
}
// Adding a breakpoint. Returns a unique id for the breakpoint.
int XDebugThreadBreakpoints::addBreakpoint(XDebugBreakpoint& bp) {
  auto const id = s_xdebug_breakpoints->m_nextBreakpointId;

  // php5 xdebug only accepts multiple breakpoints of the same type for
  // line breakpoints. A useful addition might be to allow multiple of all
  // types, but for now, multiple function or exception breakpoints results
  // in the most recent silently being used (matching php5 xdebug).
  switch (bp.type) {
    case BreakType::EXCEPTION: {
      // Remove duplicates then insert the name
      auto exceptionName = bp.exceptionName.toCppString();
      auto iter = EXCEPTION_MAP.find(exceptionName);
      if (iter != EXCEPTION_MAP.end()) {
        XDEBUG_REMOVE_BREAKPOINT(iter->second);
      }
      EXCEPTION_MAP[exceptionName] = id;
      break;
    }
    // Attempt to find the unit/line combo
    case BreakType::LINE: {
      const Unit* unit = find_unit(bp.fileName);
      if (unit == nullptr) {
        UNMATCHED.insert(id);
        break;
      }

      // If the file/line combo is invalid, throw an error
      if (!phpAddBreakPointLine(unit, bp.line)) {
        throw XDebugServer::ERROR_BREAKPOINT_INVALID;
      }
      add_line_breakpoint(id, bp, unit);
      break;
    }
    // Try to find the breakpoint's function
    case BreakType::CALL:
    case BreakType::RETURN: {
      const Class* cls = nullptr;
      const Func* func = nullptr;
      if (bp.className.isNull()) {
        func = Unit::lookupFunc(bp.funcName.get());
      } else {
        cls = Unit::lookupClass(bp.className.toString().get());
        if (cls != nullptr) {
          func = cls->lookupMethod(bp.funcName.get());
        }
      }

      // Either add the breakpoint or store it as unmatched. If the class
      // exists, we can verify that the method is valid.
      if (func != nullptr) {
        add_func_breakpoint(id, bp, func);
      } else if (!bp.className.isNull() && cls != nullptr) {
        throw XDebugServer::ERROR_BREAKPOINT_INVALID;
      } else {
        UNMATCHED.insert(id);
      }
      break;
    }
  }

  // Success, store the breakpoint and increment the id.
  BREAKPOINT_MAP[id] = bp;
  s_xdebug_breakpoints->m_nextBreakpointId++;
  return id;
}