void DebuggerProxy::processInterrupt(CmdInterrupt &cmd) {
  TRACE(2, "DebuggerProxy::processInterrupt\n");
  // Do the server-side work for this cmd, which just notifies the client.
  if (!cmd.onServer(this)) {
    Debugger::RemoveProxy(shared_from_this()); // on socket error
    return;
  }

  // Wait for commands from the debugger client and process them. We'll stay
  // here until we get a command that should cause the thread to continue.
  while (true) {
    DebuggerCommandPtr res;
    while (!DebuggerCommand::Receive(m_thrift, res,
                                     "DebuggerProxy::processInterrupt()")) {
      // we will wait forever until DebuggerClient sends us something
      checkStop();
    }
    checkStop();
    if (res) {
      // Any control flow command gets installed here and we continue execution.
      m_flow = dynamic_pointer_cast<CmdFlowControl>(res);
      if (m_flow) {
        m_flow->onServer(this);
        processFlowControl(cmd);
        if (m_flow && m_threadMode == Normal) {
          switchThreadMode(Sticky);
        }
        return;
      }
      if (res->is(DebuggerCommand::KindOfQuit)) {
        Debugger::RemoveProxy(shared_from_this());
        throw DebuggerClientExitException();
      }
    }
    try {
      // Perform the server-side work for this command.
      if (!res || !res->onServer(this)) {
        Debugger::RemoveProxy(shared_from_this());
        return;
      }
    } catch (const DebuggerException &e) {
      throw;
    } catch (...) {
      Logger::Error("onServer() throws non DebuggerException: %d",
                    res->getType());
      Debugger::RemoveProxy(shared_from_this());
      return;
    }
    if (res->shouldExitInterrupt()) {
      return;
    }
  }
}
// This gets it's own thread, and polls the client once per second to see if
// there is a signal, i.e., if the user has pressed Ctrl-C, etc. If there is a
// signal, it is passed as an interrupt to the proxy in an attempt to get other
// threads in the sandbox to stop.
//
// If another thread in the sandbox fails to stop and consume the signal then
// it will be passed to the dummy sandbox instead.
void DebuggerProxy::pollSignal() {
  TRACE(2, "DebuggerProxy::pollSignal\n");
  while (!m_stopped) {
    sleep(1);

    // after 1 second that no active thread picks up the signal, we send it
    // to dummy sandbox
    if (m_signum != CmdSignal::SignalNone && m_dummySandbox) {
      Lock lock(m_signumMutex);
      if (m_signum != CmdSignal::SignalNone) {
        m_dummySandbox->notifySignal(m_signum);
        m_signum = CmdSignal::SignalNone;
      }
    }

    Lock lock(m_signalMutex);

    // Send CmdSignal over to the client and wait for a response.
    CmdSignal cmd;
    if (!cmd.onServer(this)) break; // on socket error

    DebuggerCommandPtr res;
    while (!DebuggerCommand::Receive(m_thrift, res,
                                     "DebuggerProxy::pollSignal()")) {
      // We will wait forever until DebuggerClient sends us something.
    }
    if (!res) break;

    CmdSignalPtr sig = dynamic_pointer_cast<CmdSignal>(res);
    if (!sig) {
      Logger::Error("bad response from signal polling: %d", res->getType());
      break;
    }

    m_signum = sig->getSignal();

    if (m_signum != CmdSignal::SignalNone) {
      Debugger::RequestInterrupt(shared_from_this());
    }
  }
}
Exemple #3
0
std::string DebuggerProxy::requestAuthToken() {
  Lock lock(m_signalMutex);
  TRACE_RB(2, "DebuggerProxy::requestauthToken: sending auth request\n");

  // Try to use the current sandbox's path, defaulting to the path from
  // DebuggerDefaultSandboxPath if the current sandbox path is empty.
  auto sandboxPath = getSandbox().m_path;
  if (sandboxPath.empty()) {
    sandboxPath = RuntimeOption::DebuggerDefaultSandboxPath;
  }

  CmdAuth cmd;
  cmd.setSandboxPath(sandboxPath);
  if (!cmd.onServer(*this)) {
    TRACE_RB(2, "DebuggerProxy::requestAuthToken: "
             "Failed to send CmdAuth to client\n");
    return "";
  }

  DebuggerCommandPtr res;
  while (!DebuggerCommand::Receive(m_thrift, res,
                                   "DebuggerProxy::requestAuthToken()")) {
    checkStop();
  }
  if (!res) {
    TRACE_RB(2, "DebuggerProxy::requestAuthToken: "
             "Failed to get CmdAuth back from client\n");
    return "";
  }

  auto token = std::dynamic_pointer_cast<CmdAuth>(res);
  if (!token) {
    TRACE_RB(2, "DebuggerProxy::requestAuthToken: "
             "bad response from token request: %d", res->getType());
    return "";
  }

  return token->getToken();
}
Exemple #4
0
void DebuggerProxy::processInterrupt(CmdInterrupt &cmd) {
  TRACE_RB(2, "DebuggerProxy::processInterrupt\n");
  // Do the server-side work for this interrupt, which just notifies the client.
  if (!cmd.onServer(*this)) {
    TRACE_RB(1, "Failed to send CmdInterrupt to client\n");
    Debugger::UsageLog("server", getSandboxId(), "ProxyError",
                       "Send interrupt");
    stopAndThrow();
  }

  Debugger::UsageLogInterrupt("server", getSandboxId(), cmd);

  // Wait for commands from the debugger client and process them. We'll stay
  // here until we get a command that should cause the thread to continue.
  while (true) {
    DebuggerCommandPtr res;
    while (!DebuggerCommand::Receive(m_thrift, res,
                                     "DebuggerProxy::processInterrupt()")) {
      // we will wait forever until DebuggerClient sends us something
      checkStop();
    }
    checkStop();
    if (res) {
      TRACE_RB(2, "Proxy got cmd type %d\n", res->getType());
      Debugger::UsageLog("server", getSandboxId(),
                         folly::to<std::string>(res->getType()));
      // Any control flow command gets installed here and we continue execution.
      m_flow = std::dynamic_pointer_cast<CmdFlowControl>(res);
      if (m_flow) {
        m_flow->onSetup(*this, cmd);
        if (!m_flow->complete()) {
          TRACE_RB(2, "Incomplete flow command %d remaining on proxy for "
                   "further processing\n", m_flow->getType());
          if (m_threadMode == Normal) {
            // We want the flow command to complete on the thread that
            // starts it.
            switchThreadMode(Sticky);
          }
        } else {
          // The flow cmd has determined that it is done with its work and
          // doesn't need to remain for later processing.
          TRACE_RB(2, "Flow command %d completed\n", m_flow->getType());
          m_flow.reset();
        }
        return;
      }
      if (res->is(DebuggerCommand::KindOfQuit)) {
        TRACE_RB(2, "Received quit command\n");
        res->onServer(*this); // acknowledge receipt so that client can quit.
        stopAndThrow();
      }
    }
    bool cmdFailure = false;
    try {
      // Perform the server-side work for this command.
      if (res) {
        if (!res->onServer(*this)) {
          TRACE_RB(1, "Failed to execute cmd %d from client\n", res->getType());
          Debugger::UsageLog("server", getSandboxId(), "ProxyError",
                             "Command failed");
          cmdFailure = true;
        }
      } else {
        TRACE_RB(1, "Failed to receive cmd from client\n");
        Debugger::UsageLog("server", getSandboxId(), "ProxyError",
                           "Command receive failed");
        cmdFailure = true;
      }
    } catch (const DebuggerException &e) {
      throw;
    } catch (const Object &o) {
      Logger::Warning(DEBUGGER_LOG_TAG
                      "Cmd type %d onServer() threw a php exception %s",
                      res->getType(), o->getVMClass()->name()->data());
      Debugger::UsageLog("server", getSandboxId(), "ProxyError",
                         "Command exception");
      cmdFailure = true;
    } catch (const std::exception& e) {
      Logger::Warning(DEBUGGER_LOG_TAG
       "Cmd type %d onServer() threw exception %s", res->getType(), e.what());
      Debugger::UsageLog("server", getSandboxId(), "ProxyError",
                         "Command exception");
      cmdFailure = true;
    } catch (...) {
      Logger::Warning(DEBUGGER_LOG_TAG
       "Cmd type %d onServer() threw non standard exception", res->getType());
      Debugger::UsageLog("server", getSandboxId(), "ProxyError",
                         "Command exception");
      cmdFailure = true;
    }
    if (cmdFailure) stopAndThrow();
    if (res->shouldExitInterrupt()) return;
  }
}
Exemple #5
0
// This gets it's own thread, and polls the client once per second to see if
// there is a signal, i.e., if the user has pressed Ctrl-C, etc. If there is a
// signal, it is passed as an interrupt to the proxy in an attempt to get other
// threads in the sandbox to stop.
//
// If another thread in the sandbox fails to stop and consume the signal then
// it will be passed to the dummy sandbox instead.
void DebuggerProxy::pollSignal() {
  TRACE_RB(2, "DebuggerProxy::pollSignal: starting\n");
  int signalTimeout = RuntimeOption::DebuggerSignalTimeout;
  while (!m_stopped) {
    sleep(1);

    // Block any threads that might be interrupting from communicating with the
    // client until we're done with this poll.
    Lock lock(m_signalMutex);

    // After DebuggerSignalTimeout seconds that no active thread picks
    // up the signal, we send it to dummy sandbox.
    if ((m_signum != CmdSignal::SignalNone) && m_dummySandbox &&
        (--signalTimeout <= 0)) {
      TRACE_RB(2, "DebuggerProxy::pollSignal: sending to dummy sandbox\n");
      m_dummySandbox->notifySignal(m_signum);
      m_signum = CmdSignal::SignalNone;
    }

    // Don't actually poll if another thread is already in a command
    // processing loop with the client.
    if (!m_okayToPoll) continue;

    // Send CmdSignal over to the client and wait for a response.
    CmdSignal cmd;
    if (!cmd.onServer(*this)) {
      TRACE_RB(2, "DebuggerProxy::pollSignal: "
               "Failed to send CmdSignal to client\n");
      break;
    }

    // We've sent the client a command, and we expect an immediate
    // response. Wait 10 times to give it a chance on especially
    // overloaded computers.
    DebuggerCommandPtr res;
    for (int i = 0; i < 10; i++) {
      if (DebuggerCommand::Receive(m_thrift, res,
                                   "DebuggerProxy::pollSignal()")) break;
      if (m_stopped) {
        TRACE_RB(2, "DebuggerProxy::pollSignal: "
                 "signal thread asked to stop while waiting "
                 "for CmdSignal back from the client\n");
        break;
      }
    }
    if (!res) {
      if (!m_stopped) {
        TRACE_RB(2, "DebuggerProxy::pollSignal: "
                 "Failed to get CmdSignal back from client\n");
      }
      break;
    }

    auto sig = std::dynamic_pointer_cast<CmdSignal>(res);
    if (!sig) {
      TRACE_RB(2, "DebuggerProxy::pollSignal: "
               "bad response from signal polling: %d", res->getType());
      break;
    }

    auto newSignum = sig->getSignal();

    if (newSignum != CmdSignal::SignalNone) {
      TRACE_RB(2, "DebuggerProxy::pollSignal: "
               "got interrupt signal from client\n");
      m_signum = newSignum;
      signalTimeout = RuntimeOption::DebuggerSignalTimeout;
      Debugger::RequestInterrupt(shared_from_this());
    }
  }
  if (!m_stopped) {
    // We've noticed that the socket has closed. Stop and destory this proxy.
    TRACE_RB(2, "DebuggerProxy::pollSignal: "
             "lost communication with the client, stopping proxy\n");
    Debugger::UsageLog("server", getSandboxId(), "ProxyError", "Signal poll");
    stop();
  }
  TRACE_RB(2, "DebuggerProxy::pollSignal: ended\n");
}