void XDebugHookHandler::onFileLoad(Unit* unit) {
  // Translate the unit filename to match xdebug's internal format
  String unit_path(const_cast<StringData*>(unit->filepath()));
  String filename = File::TranslatePath(unit_path);

  // Loop over all unmatched breakpoints
  for (auto iter = UNMATCHED.begin(); iter != UNMATCHED.end();) {
    XDebugBreakpoint& bp = BREAKPOINT_MAP.at(*iter);
    if (bp.type != BreakType::LINE || bp.fileName != filename) {
      ++iter;
      continue;
    }

    // If the line is invalid there's not much we can do to inform the client
    // in the dbgp protocol at this point. php5 xdebug doesn't do anything,
    // so we just cleanup
    if (phpAddBreakPointLine(unit, bp.line)) {
      add_line_breakpoint(*iter, bp, unit);
      iter = UNMATCHED.erase(iter);
    } else {
      BREAKPOINT_MAP.erase(*iter);
      iter = UNMATCHED.erase(iter);
    }
  }
}
bool XDebugThreadBreakpoints::updateBreakpointLine(int id, int newLine) {
  auto iter = BREAKPOINT_MAP.find(id);
  if (iter == BREAKPOINT_MAP.end()) {
    return false;
  }
  XDebugBreakpoint& bp = iter->second;

  // Determine if we need to unregister the line
  auto filepath = bp.unit->filepath()->toCppString();
  if (LINE_MAP[filepath].count(bp.line) == 1) {
    phpRemoveBreakPointLine(bp.unit, bp.line);
  }

  // Register the new line
  bp.line = newLine;
  LINE_MAP[filepath].insert(std::make_pair(bp.line, id));
  phpAddBreakPointLine(bp.unit, bp.line);
  return true;
}
示例#3
0
bool XDebugThreadBreakpoints::updateBreakpointLine(int id, int newLine) {
  auto iter = BREAKPOINT_MAP.find(id);
  if (iter == BREAKPOINT_MAP.end()) {
    return false;
  }
  auto& bp = iter->second;

  // Determine if we need to unregister the line.
  auto filepath = bp.unit->filepath()->toCppString();
  if (LINE_MAP[filepath].count(bp.line) == 1) {
    phpRemoveBreakPointLine(bp.unit, bp.line);
  }

  // Register the new line.
  bp.line = tightestLoc(bp.unit, newLine).line1;
  assertx(bp.line != -1);

  LINE_MAP[filepath].emplace(bp.line, id);
  phpAddBreakPointLine(bp.unit, bp.line);
  return true;
}
// 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;
}
bool RunToLocationCommand::executeImpl(DebuggerSession* session,
                                       folly::dynamic* /*responseMsg*/
) {
  folly::dynamic& message = getMessage();
  const folly::dynamic& args = tryGetObject(message, "arguments", s_emptyArgs);
  const folly::dynamic& source = tryGetObject(args, "source", s_emptyArgs);
  const std::string& filePath = tryGetString(source, "path", "");

  if (filePath.empty()) {
    throw DebuggerCommandException(
      "Run to location source path not specified."
    );
  }

  const ClientPreferences& prefs = m_debugger->getClientPreferences();
  const std::string& path = File::TranslatePath(String(filePath)).toCppString();
  BreakpointManager* bpMgr = session->getBreakpointManager();

  int line = tryGetInt(args, "line", -1);
  if (!prefs.linesStartAt1) {
    // If client lines start at 0, make them 1 based.
    line++;
  }

  if (line <= 0) {
    throw DebuggerCommandException(
      "Invalid continue to line specified."
    );
  }

  // See if there's already a breakpoint at this file + line.
  const auto bpIds = bpMgr->getBreakpointIdsByFile(path);
  for (auto it = bpIds.begin(); it != bpIds.end(); it++) {
    Breakpoint* bp = bpMgr->getBreakpointById(*it);
    if (bp->m_line == line) {
      // There's already a breakpoint installed at the run to location.
      // Just resume the request thread and let it hit.
      return true;
    }
  }

  // Find a compilation unit to place a temp bp in.
  HPHP::String unitPath(path.c_str());
  const auto compilationUnit = lookupUnit(unitPath.get(), "", nullptr);
  if (compilationUnit == nullptr) {
    throw DebuggerCommandException(
      "Could not find a loaded compilation unit to run to location in!"
    );
  }

  std::pair<int, int> runLocation =
    m_debugger->calibrateBreakpointLineInUnit(compilationUnit, line);

  if (runLocation.first < 0) {
    throw DebuggerCommandException(
      "Could not find a suitable location in the compilation unit to run to!"
    );
  }

  if (!phpAddBreakPointLine(compilationUnit, runLocation.first)) {
    throw DebuggerCommandException(
      "Failed to install temporary breakpoint at location."
    );
  }

  RequestInfo* ri = m_debugger->getRequestInfo();
  ri->m_runToLocationInfo.path = path;
  ri->m_runToLocationInfo.line = line;

  // Tell the user where we're running to. Resolving the file path and
  // calibrating the source line could have modified things a bit.
  std::string userMsg = "Resuming request ";
  userMsg += std::to_string(targetThreadId(session));
  userMsg += " and running to resolved location ";
  userMsg += path;
  userMsg += ":";
  userMsg += std::to_string(runLocation.second);
  m_debugger->sendUserMessage(userMsg.c_str(), DebugTransport::OutputLevelInfo);

  // Resume only this request thread.
  return true;
}