Ejemplo n.º 1
0
bool XDebugServer::doCommandLoop() {
  log("Entered command loop");

  bool should_continue = false;

  // Pause the polling thread if it isn't already. (It might have paused itself
  // if it read a "break" command)
  m_pausePollingThread.store(true);

  // Unpause the polling thread when we leave the command loop.
  SCOPE_EXIT {
    m_pausePollingThread.store(false);
  };

  std::lock_guard<std::mutex> lock(m_pollingMtx);

  do {
    // If we are detached, short circuit
    if (m_status == Status::Detached) {
      return true;
    }

    // If we have no input buffered, read from socket, store into m_buffer.
    // On failure, return.
    if (m_bufferAvail == 0 && !readInput()) {
      return false;
    }

    // Initialize the response
    auto response = xdebug_xml_node_init("response");
    addXmlns(*response);

    try {
      // Parse the command and store it as the last command
      auto cmd = parseCommand();
      if (m_lastCommand != nullptr) {
        delete m_lastCommand;
      }
      m_lastCommand = cmd;

      // Try to handle the command. Possibly send a response.
      should_continue = cmd->handle(*response);
      if (cmd->shouldRespond()) {
        sendMessage(*response);
      }
    } catch (const XDebugExn& exn) {
      addError(*response, exn.error);
      sendMessage(*response);
    }

    // Free the response node.
    xdebug_xml_node_dtor(response);
  } while (!should_continue);

  return true;
}
Ejemplo n.º 2
0
bool XDebugServer::breakpoint(const Variant& filename,
                              const Variant& exception,
                              const Variant& message,
                              int line) {
  log("Hit breakpoint at %s:%d", filename.toString().data(), line);
  setStatus(Status::Break, Reason::Ok);

  // Initialize the response node
  auto response = xdebug_xml_node_init("response");
  addXmlns(*response);
  addStatus(*response);
  if (m_lastCommand != nullptr) {
    addCommand(*response, *m_lastCommand);
  }

  // Grab the c strings
  auto to_c_str = [] (const Variant& var) {
    return !var.isString() ? nullptr : var.toString().data();
  };

  auto filename_str = to_c_str(filename);
  auto exception_str = to_c_str(exception);
  auto message_str = to_c_str(message);
  auto line_str = xdebug_sprintf("%d", line);

  // Create the message node
  auto msg = xdebug_xml_node_init("xdebug:message");
  xdebug_xml_add_attribute_ex(msg, "lineno", line_str, 0, 1);
  if (filename_str != nullptr) {
    filename_str = XDebugUtils::pathToUrl(filename_str); // output file format
    xdebug_xml_add_attribute_ex(
      msg,
      "filename",
      filename_str,
      0 /* freeAttr */,
      1 /* freeVal */
    );
  }
  if (exception_str != nullptr) {
    xdebug_xml_add_attribute(msg, "exception", exception_str);
  }
  if (message_str != nullptr) {
    xdebug_xml_add_text(msg, message_str, 0);
  }

  // Add the message node then send the response
  xdebug_xml_add_child(response, msg);
  sendMessage(*response);
  xdebug_xml_node_dtor(response);

  // Wait for a resonse from the user
  return doCommandLoop();
}
Ejemplo n.º 3
0
void XDebugServer::sendStream(const char* name, const char* bytes, int len) {
  // Casts are necessary due to xml api
  auto name_str = const_cast<char*>(name);
  auto bytes_str = const_cast<char*>(bytes);

  auto message = xdebug_xml_node_init("stream");
  addXmlns(*message);
  xdebug_xml_add_attribute(message, "type", name_str);
  xdebug_xml_add_text_ex(message, bytes_str, len, 0, 1);
  sendMessage(*message);
  xdebug_xml_node_dtor(message);
}
Ejemplo n.º 4
0
void XDebugHook::onOpcode(PC pc) {
  auto server = XDEBUG_GLOBAL(Server);
  if (server == nullptr) {
    return;
  }

  // Likely case is that there was no break command.
  auto brk = server->getAndClearBreak();
  if (LIKELY(brk == nullptr)) {
    return;
  }

  server->log("Request thread received break command");

  VMRegAnchor anchor;

  auto const unit = vmfp()->func()->unit();
  auto const line = unit->getLineNumber(unit->offsetOf(pc));
  auto const filepath = const_cast<StringData*>(unit->filepath());
  auto const transpath = File::TranslatePath(String(filepath));

  // XDebugServer::breakpoint will send the response for the command before the
  // break command, but we first need to send a response for the break command.
  auto response = xdebug_xml_node_init("response");
  server->addXmlns(*response);

  auto const& cmd_str  = brk->getCommandStr();
  auto const& trans_id = brk->getTransactionId();

  // Manually add status and reason.  XDebugServer still thinks we're running
  // because we haven't run XDebugServer::breakpoint yet.
  xdebug_xml_add_attribute(response, "status", "break");
  xdebug_xml_add_attribute(response, "reason", "ok");

  // Ditto with command, XDebugServer is tracking the command before the break.
  xdebug_xml_add_attribute_dup(response, "command", cmd_str.data());
  xdebug_xml_add_attribute_dup(response, "transaction_id", trans_id.data());

  delete brk;

  server->sendMessage(*response);
  xdebug_xml_node_dtor(response);

  // Now we can go into a command loop.
  server->breakpoint(transpath, init_null(), init_null(), line);
}
Ejemplo n.º 5
0
void XDebugServer::deinitDbgp() {
  // Unless we've already stopped, send the shutdown message
  if (m_status != Status::Stopped) {
    setStatus(Status::Stopping, Reason::Ok);

    // Send the xml shutdown response
    auto response = xdebug_xml_node_init("response");
    addXmlns(*response);
    addStatus(*response);
    if (m_lastCommand != nullptr) {
      addCommand(*response, *m_lastCommand);
    }
    sendMessage(*response);
    xdebug_xml_node_dtor(response);

    // Wait for a response from the client. Regardless of the command loop
    // result, we exit.
    doCommandLoop();
  }

  // Free the input buffer & the last command
  req::free(m_buffer);
  delete m_lastCommand;
}
Ejemplo n.º 6
0
bool XDebugServer::initDbgp() {
  // Initialize the status and reason
  switch (m_mode) {
    case Mode::REQ:
      setStatus(Status::Starting, Reason::Ok);
      break;
    case Mode::JIT:
      setStatus(Status::Break, Reason::Error);
      break;
  }
  // Create the response
  auto response = xdebug_xml_node_init("init");
  addXmlns(*response);

  // Add the engine info
  auto child = xdebug_xml_node_init("engine");
  xdebug_xml_add_attribute(child, "version", XDEBUG_VERSION);
  xdebug_xml_add_text(child, XDEBUG_NAME, 0);
  xdebug_xml_add_child(response, child);

  // Add the author
  child = xdebug_xml_node_init("author");
  xdebug_xml_add_text(child, XDEBUG_AUTHOR, 0);
  xdebug_xml_add_child(response, child);

  // Add the url
  child = xdebug_xml_node_init("url");
  xdebug_xml_add_text(child, XDEBUG_URL, 0);
  xdebug_xml_add_child(response, child);

  // Add the copyright
  child = xdebug_xml_node_init("copyright");
  xdebug_xml_add_text(child, XDEBUG_COPYRIGHT, 0);
  xdebug_xml_add_child(response, child);

  // Grab the absolute path of the script filename
  auto globals = get_global_variables()->asArrayData();
  Variant scriptname_var = globals->get(s_SERVER).toArray()[s_SCRIPT_FILENAME];
  assert(scriptname_var.isString());
  auto scriptname = scriptname_var.toString().get()->mutableData();
  auto fileuri = XDebugUtils::pathToUrl(scriptname);

  // Add attributes to the root init node
  xdebug_xml_add_attribute_ex(response, "fileuri", fileuri, 0, 1);
  xdebug_xml_add_attribute(response, "language", "PHP");
  xdebug_xml_add_attribute(response, "protocol_version", DBGP_VERSION);
  xdebug_xml_add_attribute(response, "appid", getpid());

  // Add the DBGP_COOKIE environment variable
  const String dbgp_cookie = g_context->getenv(s_DBGP_COOKIE);
  if (!dbgp_cookie.empty()) {
    xdebug_xml_add_attribute(response, "session", dbgp_cookie.data());
  }

  // Add the idekey
  if (XDEBUG_GLOBAL(IdeKey).size() > 0) {
    xdebug_xml_add_attribute(response, "idekey", XDEBUG_GLOBAL(IdeKey).c_str());
  }

  // Sent the response
  sendMessage(*response);
  xdebug_xml_node_dtor(response);

  // Wait for a response from the client
  return doCommandLoop();
}