示例#1
0
std::pair<bool,Variant>
DebuggerProxy::ExecutePHP(const std::string &php, String &output,
                          int frame, int flags) {
  TRACE(2, "DebuggerProxy::ExecutePHP\n");
  // Wire up stdout and stderr to our own string buffer so we can pass
  // any output back to the client.
  StringBuffer sb;
  StringBuffer *save = g_context->swapOutputBuffer(nullptr);
  DebuggerStdoutHook stdout_hook(sb);
  DebuggerLoggerHook stderr_hook(sb);

  auto const previousEvalOutputHook = m_evalOutputHook;
  if (previousEvalOutputHook != nullptr) {
    g_context->removeStdoutHook(previousEvalOutputHook);
  }

  m_evalOutputHook = &stdout_hook;
  g_context->addStdoutHook(&stdout_hook);

  if (flags & ExecutePHPFlagsLog) {
    Logger::SetThreadHook(&stderr_hook);
  }
  SCOPE_EXIT {
    g_context->removeStdoutHook(&stdout_hook);
    g_context->swapOutputBuffer(save);
    if (flags & ExecutePHPFlagsLog) {
      Logger::SetThreadHook(nullptr);
    }

    if (previousEvalOutputHook != nullptr) {
      g_context->addStdoutHook(previousEvalOutputHook);
    }

    m_evalOutputHook = previousEvalOutputHook;
  };
  String code(php.c_str(), php.size(), CopyString);
  // We're about to start executing more PHP. This is typically done
  // in response to commands from the client, and the client expects
  // those commands to send more interrupts since, of course, the
  // user might want to debug the code we're about to run. If we're
  // already processing an interrupt, enable signal polling around
  // the execution of the new PHP to ensure that we can handle
  // signals while doing so.
  //
  // Note: we must switch the thread mode to Sticky so we block
  // other threads which may hit interrupts while we're running,
  // since nested processInterrupt() calls would normally release
  // other threads on the way out.
  assertx(m_thread == (int64_t)Process::GetThreadId());
  ThreadMode origThreadMode = m_threadMode;
  switchThreadMode(Sticky, m_thread);
  if (flags & ExecutePHPFlagsAtInterrupt) enableSignalPolling();
  SCOPE_EXIT {
    if (flags & ExecutePHPFlagsAtInterrupt) disableSignalPolling();
    switchThreadMode(origThreadMode, m_thread);
  };
  auto const ret = g_context->evalPHPDebugger(code.get(), frame);
  output = sb.detach();
  return {ret.failed, ret.result};
}
示例#2
0
// Handle an interrupt from the VM.
void DebuggerProxy::interrupt(CmdInterrupt &cmd) {
  TRACE_RB(2, "DebuggerProxy::interrupt\n");
  // Make any breakpoints that have passed breakable again.
  setBreakableForBreakpointsNotMatching(cmd);

  // At this point we have an interrupt, but we don't know if we're on the
  // thread the proxy considers "current".
  // NB: BreakPointReached really means we've got control of a VM thread from
  // the opcode hook. This could be for a breakpoint, stepping, etc.

  // Wait until this thread is the one this proxy wants to debug.
  if (!blockUntilOwn(cmd, true)) return;

  // We know we're on the "current" thread, so we can process any active flow
  // command, stop if we're at a breakpoint, handle other interrupts, etc.
  if (checkFlowBreak(cmd)) {
    // We've hit a breakpoint and now need to make sure that breakpoints
    // won't be hit again for this site until control leaves this site.
    // (Breakpoints can still get hit if control reaches this site during
    // a call that is part of this site because the flags are stacked.)
    unsetBreakableForBreakpointsMatching(cmd);
    while (true) {
      try {
        // We're about to send the client an interrupt and start
        // waiting for commands back from it. Disable signal polling
        // during this time, since our protocol requires that only one
        // thread talk to the client at a time.
        disableSignalPolling();
        SCOPE_EXIT { enableSignalPolling(); };
        processInterrupt(cmd);
      } catch (const DebuggerException &e) {
        TRACE(2, "DebuggerException from processInterrupt!\n");
        switchThreadMode(Normal);
        throw;
      } catch (...) {
        TRACE(2, "Unknown exception from processInterrupt!\n");
        assertx(false); // no other exceptions should be seen here
        switchThreadMode(Normal);
        throw;
      }
      if (cmd.getInterruptType() == PSPEnded) break;
      if (!m_newThread) break; // we're not switching threads
      switchThreadMode(Normal, m_newThread->m_id);
      m_newThread.reset();
      blockUntilOwn(cmd, false);
    }
  }

  if ((m_threadMode == Normal) || (cmd.getInterruptType() == PSPEnded)) {
    // If the thread mode is Normal we let other threads with
    // interrupts go ahead and process them. We also do this when the
    // thread is at PSPEnded because the thread is done.
    switchThreadMode(Normal);
  }
}
Variant DebuggerProxy::ExecutePHP(const std::string &php, String &output,
                                  int frame, int flags) {
  TRACE(2, "DebuggerProxy::ExecutePHP\n");
  Variant ret;
  // Wire up stdout and stderr to our own string buffer so we can pass
  // any output back to the client.
  StringBuffer sb;
  StringBuffer *save = g_context->swapOutputBuffer(nullptr);
  g_context->setStdout(append_stdout, &sb);
  if (flags & ExecutePHPFlagsLog) {
    Logger::SetThreadHook(append_stderr, &sb);
  }
  try {
    String code(php.c_str(), php.size(), CopyString);
    // @TODO: enable this once task #2608250 is completed.
#if 0
    // We're about to start executing more PHP. This is typically done
    // in response to commands from the client, and the client expects
    // those commands to send more interrupts since, of course, the
    // user might want to debug the code we're about to run. If we're
    // already processing an interrupt, enable signal polling around
    // the execution of the new PHP to ensure that we can handle
    // signals while doing so.
    if (flags & ExecutePHPFlagsAtInterrupt) enableSignalPolling();
    SCOPE_EXIT {
      if (flags & ExecutePHPFlagsAtInterrupt) disableSignalPolling();
    };
#endif
    g_vmContext->evalPHPDebugger((TypedValue*)&ret, code.get(), frame);
  } catch (InvalidFunctionCallException &e) {
    sb.append(Debugger::ColorStderr(String(e.what())));
    sb.append(Debugger::ColorStderr(
              "You may also need to connect to a host "
              "(e.g., machine connect localhost)."));
  } catch (Exception &e) {
    sb.append(Debugger::ColorStderr(String(e.what())));
  } catch (Object &e) {
    try {
      sb.append(Debugger::ColorStderr(e.toString()));
    } catch (BadTypeConversionException &e) {
      sb.append(Debugger::ColorStderr
                (String("(object without __toString() is thrown)")));
    }
  } catch (...) {
    sb.append(Debugger::ColorStderr(String("(unknown exception was thrown)")));
  }
  g_context->setStdout(nullptr, nullptr);
  g_context->swapOutputBuffer(save);
  if (flags & ExecutePHPFlagsLog) {
    Logger::SetThreadHook(nullptr, nullptr);
  }
  output = sb.detach();
  return ret;
}
示例#4
0
Variant DebuggerProxy::ExecutePHP(const std::string &php, String &output,
                                  int frame, bool &failed, int flags) {
  TRACE(2, "DebuggerProxy::ExecutePHP\n");
  Variant ret;
  // Wire up stdout and stderr to our own string buffer so we can pass
  // any output back to the client.
  StringBuffer sb;
  StringBuffer *save = g_context->swapOutputBuffer(nullptr);
  g_context->setStdout(append_stdout, &sb);
  if (flags & ExecutePHPFlagsLog) {
    Logger::SetThreadHook(append_stderr, &sb);
  }
  String code(php.c_str(), php.size(), CopyString);
  // We're about to start executing more PHP. This is typically done
  // in response to commands from the client, and the client expects
  // those commands to send more interrupts since, of course, the
  // user might want to debug the code we're about to run. If we're
  // already processing an interrupt, enable signal polling around
  // the execution of the new PHP to ensure that we can handle
  // signals while doing so.
  //
  // Note: we must switch the thread mode to Sticky so we block
  // other threads which may hit interrupts while we're running,
  // since nested processInterrupt() calls would normally release
  // other threads on the way out.
  assert(m_thread == (int64_t)Process::GetThreadId());
  ThreadMode origThreadMode = m_threadMode;
  switchThreadMode(Sticky, m_thread);
  if (flags & ExecutePHPFlagsAtInterrupt) enableSignalPolling();
  SCOPE_EXIT {
    if (flags & ExecutePHPFlagsAtInterrupt) disableSignalPolling();
    switchThreadMode(origThreadMode, m_thread);
  };
  failed = g_vmContext->evalPHPDebugger((TypedValue*)&ret, code.get(), frame);
  g_context->setStdout(nullptr, nullptr);
  g_context->swapOutputBuffer(save);
  if (flags & ExecutePHPFlagsLog) {
    Logger::SetThreadHook(nullptr, nullptr);
  }
  output = sb.detach();
  return ret;
}