Example #1
0
void XDebugProfiler::ensureBufferSpace() {
  if (m_nextFrameIdx < m_frameBufferSize) {
    return;
  }

  // The initial buffer size is 0
  int64_t new_buf_size = (m_frameBufferSize == 0)?
    XDEBUG_GLOBAL(FramebufSize) :
    m_frameBufferSize * XDEBUG_GLOBAL(FramebufExpansion);

  try {
    int64_t new_buf_bytes = new_buf_size * sizeof(FrameData);
    m_frameBuffer = (FrameData*) smart_realloc((void*) m_frameBuffer,
                                               new_buf_bytes);
    m_frameBufferSize = new_buf_size;
  } catch (const OutOfMemoryException& e) {
    raise_error("Cannot allocate more memory for the xdebug profiler. Consider "
                "turning off profiling or tracing. Note that certain ini "
                "settings such as hhvm.xdebug.collect_memory and "
                "hhvm.xdebug.collect_time implicitly "
                "turn on tracing, so turn those off if this is unexpected.\n"
                "Current frame buffer length: %zu\n"
                "Failed to expand to length: %zu\n",
                m_frameBufferSize,
                new_buf_size);
  }
}
Example #2
0
// Starts tracing using the given profiler
static void start_tracing(XDebugProfiler* profiler,
                          folly::StringPiece filename = folly::StringPiece(),
                          int64_t options = 0) {
  // Add ini settings
  if (XDEBUG_GLOBAL(TraceOptions)) {
    options |= k_XDEBUG_TRACE_APPEND;
  }
  if (XDEBUG_GLOBAL(TraceFormat) == 1) {
    options |= k_XDEBUG_TRACE_COMPUTERIZED;
  }
  if (XDEBUG_GLOBAL(TraceFormat) == 2) {
    options |= k_XDEBUG_TRACE_HTML;
  }

  // If no filename is passed, php5 xdebug stores in the default output
  // directory with the default file name.
  folly::StringPiece dirname;

  if (filename.empty()) {
    auto& default_dirname = XDEBUG_GLOBAL(TraceOutputDir);
    auto& default_filename = XDEBUG_GLOBAL(TraceOutputName);

    dirname = folly::StringPiece(default_dirname);
    filename = folly::StringPiece(default_filename);
  }

  auto const suffix = !(options & k_XDEBUG_TRACE_NAKED_FILENAME);
  auto abs_filename = format_filename(dirname, filename, suffix);
  profiler->enableTracing(abs_filename, options);
}
Example #3
0
void XDebugProfiler::collectFrameData(FrameData& frameData,
                                      const TypedValue* retVal) {
  VMRegAnchor _; // Ensure consistent state for vmfp and vmpc
  ActRec* fp = vmfp();
  bool is_func_begin = retVal == nullptr;
  frameData.is_func_begin = is_func_begin;

  // The function reference and call file/line are stored when tracing/profiling
  // on function enter
  if ((m_tracingEnabled || m_profilingEnabled) && is_func_begin) {
    frameData.func = fp->func();

    // Need the previous frame in order to get the call line. If we cannot
    // get the previous frame, default to 1
    Offset offset;
    const ActRec* prevFp = g_context->getPrevVMState(fp, &offset);
    if (prevFp != nullptr) {
      frameData.line = prevFp->unit()->getLineNumber(offset);
    } else {
      frameData.line = 1;
    }
  } else {
    frameData.func = nullptr;
    frameData.line = 1;
  }

  // Time is stored if profiling, tracing, or collect_time is enabled, but it
  // only needs to be collected on function exit if profiling or if computerized
  // tracing output is enabled
  if (m_profilingEnabled ||
      (is_func_begin && (m_collectTime || m_tracingEnabled)) ||
      (m_tracingEnabled && (m_tracingOpts & k_XDEBUG_TRACE_COMPUTERIZED))) {
    frameData.time = Timer::GetCurrentTimeMicros();
  } else {
    frameData.time = 0;
  }

  // Memory usage is stored on function begin if tracing, or if collect_memory
  // is enabled, or on function end if computerized tracing output is enabled
  if ((is_func_begin && (m_tracingEnabled || m_collectMemory)) ||
      (m_tracingEnabled && (m_tracingOpts & k_XDEBUG_TRACE_COMPUTERIZED))) {
    frameData.memory_usage = MM().getStats().usage;
  } else {
    frameData.memory_usage = 0;
  }

  // If tracing is enabled, we may need to collect a serialized version of
  // the arguments or the return value.
  if (m_tracingEnabled && is_func_begin && XDEBUG_GLOBAL(CollectParams) > 0) {
    // TODO(#3704) This relies on xdebug_var_dump
    throw_not_implemented("Tracing with collect_params enabled");
  } else if (m_tracingEnabled && !is_func_begin &&
             XDEBUG_GLOBAL(CollectReturn)) {
    // TODO(#3704) This relies on xdebug_var_dump
    throw_not_implemented("Tracing with collect_return enabled");
  } else {
    frameData.context_str = nullptr;
  }
}
void XDebugHookHandler::onFlowBreak(const Unit* unit, int line) {
  if (XDEBUG_GLOBAL(Server) != nullptr) {
    // Translate the unit filepath and then break
    auto const filepath = String(const_cast<StringData*>(unit->filepath()));
    auto const transpath = File::TranslatePath(filepath);
    XDEBUG_GLOBAL(Server)->breakpoint(transpath, init_null(),
                                      init_null(), line);
  }
}
void XDebugHookHandler::onFlowBreak(const Unit* unit, int line) {
  if (XDEBUG_GLOBAL(Server) != nullptr) {
    // Translate the unit filepath and then break
    const String filepath = String(unit->filepath()->data(), CopyString);
    const String transpath = File::TranslatePath(filepath);
    XDEBUG_GLOBAL(Server)->breakpoint(transpath, init_null(),
                                      init_null(), line);
  }
}
Example #6
0
void XDebugServer::attach(Mode mode) {
  assert(XDEBUG_GLOBAL(Server) == nullptr);
  try {
    XDEBUG_GLOBAL(Server) = new XDebugServer(mode);
    if (!XDEBUG_GLOBAL(Server)->initDbgp()) {
      detach();
    }
  } catch (...) {
    // Fail silently, continue running the request.
  }
}
Example #7
0
bool XDebugServer::isNeeded() {
  if (!XDEBUG_GLOBAL(RemoteEnable) ||
      XDEBUG_GLOBAL(RemoteMode) == "jit") {
    return false;
  } else if (XDEBUG_GLOBAL(RemoteAutostart)) {
    return true;
  } else {
    // Check $_COOKIE[XDEBUG_SESSION]
    const ArrayData* globals = get_global_variables()->asArrayData();
    Array cookie = globals->get(s_COOKIE).toArray();
    return !cookie[s_SESSION].isNull();
  }
}
Example #8
0
void HHVM_FUNCTION(
  xdebug_var_dump,
  const Variant& v,
  const Array& _argv /* = null_array */
) {
  if (!XDEBUG_GLOBAL(OverloadVarDump) || !XDEBUG_GLOBAL(DefaultEnable)) {
    HHVM_FN(var_dump)(v, _argv);
    return;
  }

  do_var_dump(v);
  auto const size = _argv.size();
  for (int64_t i = 0; i < size; ++i) {
    do_var_dump(_argv[i]);
  }
}
Example #9
0
  XDEBUG_NOTIMPLEMENTED

static Variant HHVM_FUNCTION(xdebug_start_trace,
                             const Variant& traceFileVar,
                             int64_t options /* = 0 */) {
  // Allowed to pass null.
  folly::StringPiece trace_file;
  if (traceFileVar.isString()) {
    // We're not constructing a new String, we're just using the one in
    // traceFileVar, so this is safe.
    trace_file = traceFileVar.toString().slice();
  }

  // Initialize the profiler if it isn't already.
  if (!XDEBUG_GLOBAL(ProfilerAttached)) {
    attach_xdebug_profiler();
  }

  // php5 xdebug returns false when tracing already started.
  auto profiler = xdebug_profiler();
  if (profiler->isTracing()) {
    return false;
  }

  // Start tracing, then grab the current begin frame
  start_tracing(profiler, trace_file, options);
  profiler->beginFrame(nullptr);
  return HHVM_FN(xdebug_get_tracefile_name)();
}
Example #10
0
static void HHVM_FUNCTION(_xdebug_check_trigger_vars) {
  if (XDebugExtension::Enable &&
      XDebugProfiler::isAttachNeeded() &&
      !XDEBUG_GLOBAL(ProfilerAttached)) {
    attach_xdebug_profiler();
  }
}
Example #11
0
// Detaches the xdebug profiler if it's no longer needed
static void detach_xdebug_profiler_if_needed() {
  assert(XDEBUG_GLOBAL(ProfilerAttached));
  auto profiler = xdebug_profiler();
  if (!profiler->isNeeded()) {
    detach_xdebug_profiler();
  }
}
Example #12
0
// Starts profiling using the given profiler
static void start_profiling(XDebugProfiler* profiler) {
  // Add ini options
  int64_t opts = 0;
  if (XDEBUG_GLOBAL(ProfilerAppend)) {
    opts |= k_XDEBUG_PROFILE_APPEND;
  }

  // Create the filename then enable
  auto& dirname = XDEBUG_GLOBAL(ProfilerOutputDir);
  auto& filename = XDEBUG_GLOBAL(ProfilerOutputName);
  auto dirname_slice = folly::StringPiece{dirname};
  auto filename_slice = folly::StringPiece{filename};

  auto abs_filename = format_filename(dirname_slice, filename_slice, false);

  profiler->enableProfiling(abs_filename, opts);
}
Example #13
0
void XDebugServer::onRequestInit() {
  if (!XDEBUG_GLOBAL(RemoteEnable)) {
    return;
  }

  // Need to turn on debugging regardless of the remote mode in order to
  // capture exceptions/errors
  if (!DebugHookHandler::attach<XDebugHookHandler>()) {
    raise_warning("Could not attach xdebug remote debugger to the current "
                  "thread. A debugger is already attached.");
    return;
  }

  // Grab $_GET, $_COOKIE, and the transport
  const ArrayData* globals = get_global_variables()->asArrayData();
  Array get = globals->get(s_GET).toArray();
  Array cookie = globals->get(s_COOKIE).toArray();
  Transport* transport = g_context->getTransport();

  // Need to check $_GET[XDEBUG_SESSION_STOP]. If set, delete the session
  // cookie
  const Variant sess_stop_var = get[s_SESSION_STOP];
  if (!sess_stop_var.isNull()) {
    cookie.set(s_SESSION, init_null());
    if (transport != nullptr) {
      transport->setCookie(s_SESSION, empty_string());
    }
  }

  // Need to check $_GET[XDEBUG_SESSION_START]. If set, store the session
  // cookie with $_GET[XDEBUG_SESSION_START] as the value
  const Variant sess_start_var = get[s_SESSION_START];
  if (sess_start_var.isString()) {
    String sess_start = sess_start_var.toString();
    cookie.set(s_SESSION,  sess_start);
    if (transport != nullptr) {
      int64_t expire = XDEBUG_GLOBAL(RemoteCookieExpireTime);
      if (expire > 0) {
        timespec ts;
        Timer::GetRealtimeTime(ts);
        expire += ts.tv_sec;
      }
      transport->setCookie(s_SESSION, sess_start, expire);
    }
  }
}
Example #14
0
static Variant HHVM_FUNCTION(xdebug_get_tracefile_name) {
  if (XDEBUG_GLOBAL(ProfilerAttached)) {
    auto profiler = xdebug_profiler();
    if (profiler->isTracing()) {
      return profiler->getTracingFilename();
    }
  }
  return false;
}
Example #15
0
void HHVM_FUNCTION(var_dump, const Variant& expression,
                             const Array& _argv /*=null_array */) {
#ifdef ENABLE_EXTENSION_XDEBUG
  if (UNLIKELY(XDEBUG_GLOBAL(OverloadVarDump) &&
               XDEBUG_GLOBAL(DefaultEnable))) {
    HHVM_FN(xdebug_var_dump)(expression, _argv);
    return;
  }
#endif

  VariableSerializer vs(VariableSerializer::Type::VarDump, 0, 2);
  do_var_dump(vs, expression);

  auto sz = _argv.size();
  for (int i = 0; i < sz; i++) {
    do_var_dump(vs, _argv[i]);
  }
}
Example #16
0
// If the xdebug profiler is attached, ensures we still need it. If it is not
// attached, check if we need it.
static void refresh_xdebug_profiler() {
  if (XDEBUG_GLOBAL(ProfilerAttached)) {
    detach_xdebug_profiler_if_needed();
  } else if (XDebugProfiler::isCollectionNeeded()) {
    // We know that the profiler is not attached, so either we failed the
    // request init checks or it was turned off during runtime. So we
    // only want to turn on profiling if collection is now needed.
    attach_xdebug_profiler();
  }
}
Example #17
0
// Attempts to attach the xdebug profiler to the current thread. Assumes it
// is not already attached. Raises an error on failure.
static void attach_xdebug_profiler() {
  assert(!XDEBUG_GLOBAL(ProfilerAttached));
  if (s_profiler_factory->start(ProfilerKind::XDebug, 0, false)) {
    XDEBUG_GLOBAL(ProfilerAttached) = true;
    // Enable profiling and tracing if we need to
    auto profiler = xdebug_profiler();
    if (XDebugProfiler::isProfilingNeeded()) {
      start_profiling(profiler);
    }
    if (XDebugProfiler::isTracingNeeded()) {
      start_tracing(profiler);
    }
    profiler->setCollectMemory(XDEBUG_GLOBAL(CollectMemory));
    profiler->setCollectTime(XDEBUG_GLOBAL(CollectTime));
    profiler->setMaxNestingLevel(XDEBUG_GLOBAL(MaxNestingLevel));
  } else {
    raise_error("Could not start xdebug profiler. Another profiler is "
                "likely already attached to this thread.");
  }
}
Example #18
0
void XDebugExtension::requestShutdown() {
  if (!Enable) {
    return;
  }

  s_xdebug_breakpoints.destroy();

  // Potentially kill the profiler
  if (XDEBUG_GLOBAL(ProfilerAttached)) {
    detach_xdebug_profiler();
  }
}
Example #19
0
void XDebugServer::onRequestInit() {
  if (!XDEBUG_GLOBAL(RemoteEnable)) {
    return;
  }

  // TODO(#4489053) Enable this when debugger internals have been refactored
  // Need to turn on debugging regardless of the remote mode in order to
  // capture exceptions/errors
  // ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
  // ti->m_reqInjectionData.setDebugger(true);

  // Grab $_GET, $_COOKIE, and the transport
  const ArrayData* globals = get_global_variables()->asArrayData();
  Array get = globals->get(s_GET).toArray();
  Array cookie = globals->get(s_COOKIE).toArray();
  Transport* transport = g_context->getTransport();

  // Need to check $_GET[XDEBUG_SESSION_STOP]. If set, delete the session
  // cookie
  const Variant sess_stop_var = get[s_SESSION_STOP];
  if (!sess_stop_var.isNull()) {
    cookie.set(s_SESSION, init_null());
    if (transport != nullptr) {
      transport->setCookie(s_SESSION, empty_string());
    }
  }

  // Need to check $_GET[XDEBUG_SESSION_START]. If set, store the session
  // cookie with $_GET[XDEBUG_SESSION_START] as the value
  const Variant sess_start_var = get[s_SESSION_START];
  if (sess_start_var.isString()) {
    String sess_start = sess_start_var.toString();
    cookie.set(s_SESSION,  sess_start);
    if (transport != nullptr) {
      transport->setCookie(s_SESSION,
                           sess_start,
                           XDEBUG_GLOBAL(RemoteCookieExpireTime));
    }
  }
}
Example #20
0
  XDEBUG_NOTIMPLEMENTED

Variant HHVM_FUNCTION(xdebug_get_profiler_filename) {
  if (!XDEBUG_GLOBAL(ProfilerAttached)) {
    return false;
  }

  auto profiler = xdebug_profiler();
  if (profiler->isProfiling()) {
    return profiler->getProfilingFilename();
  }
  return false;
}
Example #21
0
static void do_var_dump(const Variant& v) {
  auto const cli = XDEBUG_GLOBAL(CliColor);
  XDebugExporter exporter;
  exporter.max_depth = XDEBUG_GLOBAL(VarDisplayMaxDepth);
  exporter.max_children = XDEBUG_GLOBAL(VarDisplayMaxChildren);
  exporter.max_data = XDEBUG_GLOBAL(VarDisplayMaxData);
  exporter.page = 0;

  String str;

  auto const html_errors =
    ThreadInfo::s_threadInfo->m_reqInjectionData.hasHtmlErrors();
  if (html_errors) {
    str = xdebug_get_zval_value_fancy(v, exporter);
  } else if ((cli == 1 && is_output_tty()) || cli == 2) {
    str = xdebug_get_zval_value_ansi(v, exporter);
  } else {
    str = xdebug_get_zval_value_text(v, exporter);
  }

  g_context->write(str);
}
Example #22
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);
    }
  }
}
Example #23
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);
}
Example #24
0
static bool HHVM_FUNCTION(xdebug_break) {
  auto server = XDEBUG_GLOBAL(Server);
  if (server == nullptr) {
    return false;
  }

  // Breakpoint displays the current file/line number
  auto file = g_context->getContainingFileName();
  auto filename = file == nullptr ? empty_string() : String(file);
  auto const line = g_context->getLine();

  // Attempt to perform the breakpoint, detach the server if something goes
  // wrong
  if (!server->breakpoint(filename, init_null(), init_null(), line)) {
    XDebugServer::detach();
  }
  return true;
}
Example #25
0
  XDEBUG_NOTIMPLEMENTED

static Variant HHVM_FUNCTION(xdebug_stop_trace) {
  if (!XDEBUG_GLOBAL(ProfilerAttached)) {
    return false;
  }

  auto profiler = xdebug_profiler();
  if (!profiler->isTracing()) {
    return false;
  }

  // End with xdebug_stop_trace()
  profiler->endFrame(init_null().asTypedValue(), nullptr, false);
  auto filename = profiler->getTracingFilename();
  profiler->disableTracing();
  detach_xdebug_profiler_if_needed();
  return filename;
}
Example #26
0
void XDebugProfiler::writeTracingMemory(int64_t memory) {
  switch (outputType) {
    case TraceOutputType::NORMAL:
      fprintf(m_tracingFile, "%10lu ", memory);
      // Delta Memory (since the last begin frame)
      if (XDEBUG_GLOBAL(ShowMemDelta)) {
        if (m_tracingPrevBegin != nullptr) {
          int64_t prev_usage = m_tracingPrevBegin->memory_usage;
          fprintf(m_tracingFile, "%+8ld", memory - prev_usage);
        } else {
          fprintf(m_tracingFile, "        ");
        }
      }
      break;
    case TraceOutputType::HTML:
      fprintf(m_tracingFile, "<td align='right'>%ld</td>", memory);
      break;
    case TraceOutputType::COMPUTERIZED:
      fprintf(m_tracingFile, "%lu\t", memory);
      break;
  }
}
Example #27
0
XDebugServer::XDebugServer(Mode mode) : m_mode(mode) {
  // Attempt to open optional log file
  if (XDEBUG_GLOBAL(RemoteLog).size() > 0) {
    m_logFile = fopen(XDEBUG_GLOBAL(RemoteLog).c_str(), "a");
    if (m_logFile == nullptr) {
      raise_warning("XDebug could not open the remote debug file '%s'.",
                    XDEBUG_GLOBAL(RemoteLog).c_str());
    }

    log("Log opened at");
    XDebugUtils::fprintTimestamp(m_logFile);
    log("\n");
    logFlush();
  }

  // Grab the hostname and port to connect to
  const char* hostname = XDEBUG_GLOBAL(RemoteHost).c_str();
  int port = XDEBUG_GLOBAL(RemotePort);
  if (XDEBUG_GLOBAL(RemoteConnectBack)) {
    const ArrayData* globals = get_global_variables()->asArrayData();
    Array server = globals->get(s_SERVER).toArray();
    log("I: Checking remote connect back address.\n");

    // Grab $_SERVER[HTTP_X_FORWARDED_FOR] then $_SERVER[REMOTE_ADDR]
    bool host_found = true;
    if (server[s_HTTP_X_FORWARDED_FOR].isString()) {
      hostname = server[s_HTTP_X_FORWARDED_FOR].toString().data();
    } else if (server[s_REMOTE_ADDR].isString()) {
      hostname = server[s_REMOTE_ADDR].toString().data();
    } else {
      host_found = false;
    }

    // Did we find a host?
    if (host_found) {
      log("I: Remote address found, connecting to %s:%d.\n", hostname, port);
    } else {
      log("W: Remote address not found, connecting to configured address/port: "
          "%s:%d. :-|\n", hostname, port);
    }
  } else {
    log("I: Connecting to configured address/port: %s:%d.\n", hostname, port);
  }

  // Create the socket
  m_socket = createSocket(hostname, port);
  if (m_socket == -1) {
    log("E: Could not connect to client. :-(\n");
    goto failure;
  } else if (m_socket == -2) {
    log("E: Time-out connecting to client. :-(\n");
    goto failure;
  }

  // Get the requested handler
  log("I: Connected to client. :-)\n");
  if (XDEBUG_GLOBAL(RemoteHandler) != "dbgp") {
    log("E: The remote debug handler '%s' is not supported. :-(\n",
        XDEBUG_GLOBAL(RemoteHandler).c_str());
    goto failure;
  }

  // php5 xdebug has an error case here, but initializing the dbgp handler never
  // actually fails
  initDbgp();
  return;

// Failure cleanup. A goto is used to prevent duplication
failure:
  destroySocket();
  closeLog();
  // Allows the guarantee that any instance of an xdebug server is valid
  throw Exception("XDebug Server construction failed");
}
Example #28
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();
}
Example #29
0
void XDebugServer::detach() {
  assert(XDEBUG_GLOBAL(Server) != nullptr);
  XDEBUG_GLOBAL(Server)->deinitDbgp();
  delete XDEBUG_GLOBAL(Server);
  XDEBUG_GLOBAL(Server) = nullptr;
}
Example #30
0
int XDebugServer::createSocket(const char* hostname, int port) {
  // Grab the address info
  sockaddr sa;
  sockaddr_in address;
  memset(&address, 0, sizeof(address));
  address.sin_family = AF_INET;
  address.sin_port = htons((unsigned short) port);
  lookupHostname(hostname, address.sin_addr);

  // Create the socket
  auto const sockfd = socket(address.sin_family, SOCK_STREAM, 0);
  if (sockfd == -1) {
    log("create_debugger_socket(\"%s\", %d) socket: %s\n",
        hostname, port, folly::errnoStr(errno).c_str());
    return -1;
  }

  // Put socket in non-blocking mode so we can use poll() for timeouts.
  fcntl(sockfd, F_SETFL, O_NONBLOCK);

  // Do the connect.
  auto const status = connect(sockfd, (sockaddr*)(&address), sizeof(address));
  if (status < 0) {
    if (errno != EINPROGRESS) {
      return -1;
    }

    double timeout_sec = XDEBUG_GLOBAL(RemoteTimeout);

    // Loop until request has completed or there is an error.
    while (true) {
      pollfd pollfd;
      pollfd.fd = sockfd;
      pollfd.events = POLLIN | POLLOUT;
      pollfd.revents = 0;

      // Wait for a socket event.
      auto const status = poll(&pollfd, 1, (int)(timeout_sec * 1000.0));

      // Timeout.
      if (status == 0) return -2;
      // Error.
      if (status == -1) return -1;

      // Error or hang-up.
      if ((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP)) return -1;

      // Success.
      if ((pollfd.revents & POLLIN) || (pollfd.revents & POLLOUT)) break;
    }

    // Ensure we're actually connected.
    socklen_t sa_size = sizeof(sa);
    if (getpeername(sockfd, &sa, &sa_size) == -1) {
      return -1;
    }
  }

  // Reset the socket to non-blocking mode and setup socket options
  fcntl(sockfd, F_SETFL, 0);
  long optval = 1;
  setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
  return sockfd;
}