Beispiel #1
0
// Ask every thread in this proxy's sandbox and the dummy sandbox to "stop".
// Gaining control of these threads is the intention... the mechanism is to
// force them all to start interpreting all of their code in an effort to gain
// control in phpDebuggerOpcodeHook().
void Debugger::requestInterrupt(DebuggerProxyPtr proxy) {
  TRACE(2, "Debugger::requestInterrupt\n");
  const StringData* sid = StringData::GetStaticString(proxy->getSandboxId());
  FOREACH_SANDBOX_THREAD_BEGIN(sid, ti)
    ti->m_reqInjectionData.setDebuggerIntr(true);
  FOREACH_SANDBOX_THREAD_END()

  sid = StringData::GetStaticString(proxy->getDummyInfo().id());
  FOREACH_SANDBOX_THREAD_BEGIN(sid, ti)
    ti->m_reqInjectionData.setDebuggerIntr(true);
  FOREACH_SANDBOX_THREAD_END()
}
Beispiel #2
0
Array HHVM_FUNCTION(debugger_get_info) {
  Array ret(Array::Create());
  if (!RuntimeOption::EnableDebugger) return ret;
  DebuggerProxyPtr proxy = Debugger::GetProxy();
  if (!proxy) return ret;
  Variant address;
  Variant port;
  if (proxy->getClientConnectionInfo(ref(address), ref(port))) {
    ret.set(s_clientIP, address);
    ret.set(s_clientPort, port);
  }
  return ret;
}
Beispiel #3
0
void Debugger::updateProxySandbox(DebuggerProxyPtr proxy,
                                  const StringData* sandboxId) {
  // update proxy's sandbox info from what is on file
  SandboxMap::const_accessor acc;
  if (m_sandboxMap.find(acc, sandboxId)) {
    proxy->updateSandbox(acc->second);
  } else {
    // don't have the sandbox on file yet. create a sandbox info with
    // no path for now. Sandbox path will be updated upon first request
    // with that sandbox arrives
    DSandboxInfoPtr sb(new DSandboxInfo(sandboxId->toCPPString()));
    proxy->updateSandbox(sb);
  }
}
Beispiel #4
0
void Debugger::removeProxy(DebuggerProxyPtr proxy) {
  if (proxy->getSandbox().valid()) {
    const StringData* sid = StringData::GetStaticString(proxy->getSandboxId());
    setDebuggerFlag(sid, false);
    m_proxyMap.erase(sid);
  }
  const StringData* dummySid =
    StringData::GetStaticString(proxy->getDummyInfo().id());
  m_proxyMap.erase(dummySid);
  // Clear the debugger blacklist PC upon last detach if JIT is used
  if (RuntimeOption::EvalJit && countConnectedProxy() == 0) {
    VM::Transl::Translator::Get()->clearDbgBL();
  }
}
Beispiel #5
0
// NB: when this returns, the Debugger class no longer has any references to the
// given proxy. It will likely be destroyed when the caller's reference goes out
// of scope.
void Debugger::removeProxy(DebuggerProxyPtr proxy) {
  TRACE(2, "Debugger::removeProxy\n");
  if (proxy->getSandbox().valid()) {
    const StringData* sid = makeStaticString(proxy->getSandboxId());
    setDebuggerFlag(sid, false);
    m_proxyMap.erase(sid);
  }
  const StringData* dummySid =
    makeStaticString(proxy->getDummyInfo().id());
  m_proxyMap.erase(dummySid);
  // Clear the debugger blacklist PC upon last detach if JIT is used
  if (RuntimeOption::EvalJit && countConnectedProxy() == 0) {
    jit::mcg->tx().clearDbgBL();
  }
}
Beispiel #6
0
bool Debugger::switchSandbox(DebuggerProxyPtr proxy,
                             const std::string &newId,
                             bool force) {
  const StringData* newSid = StringData::GetStaticString(newId);
  // Lock the proxy during the switch
  Lock l(proxy.get());
  if (proxy->getSandboxId() != newId) {
    if (!switchSandboxImpl(proxy, newSid, force)) {
      // failed to switch
      return false;
    }
  }

  updateProxySandbox(proxy, newSid);
  return true;
}
Beispiel #7
0
bool Debugger::switchSandboxImpl(DebuggerProxyPtr proxy,
                                 const StringData* newSid,
                                 bool force) {
  TRACE(2, "Debugger::switchSandboxImpl\n");

  // When attaching to the sandbox, ensure that hphpd is the active debugger.
  // If this fails, we'll end up returning failure to the CmdMachine on the
  // hphpd client that attempted the attach, and it will inform the user.
  auto instance = HphpdHook::GetInstance();
  if (!DebuggerHook::setActiveDebuggerInstance(instance, true)) {
    return false;
  }

  // Take the new sandbox
  DebuggerProxyPtr otherProxy;
  {
    ProxyMap::accessor acc;
    if (m_proxyMap.insert(acc, newSid)) {
      acc->second = proxy;
    } else {
      // the sandbox indicated by newId is already attached by another proxy
      if (!force) {
        return false;
      }
      // Delay the destruction of the proxy originally attached to the sandbox
      // to avoid calling a DebuggerProxy destructor (which can sleep) while
      // holding m_mutex.
      otherProxy = acc->second;
      acc->second = proxy;
    }
  }

  if (proxy->getSandbox().valid()) {
    // Detach from the old sandbox
    const StringData* oldSid =
      makeStaticString(proxy->getSandboxId());
    setDebuggerFlag(oldSid, false);
    m_proxyMap.erase(oldSid);
  }
  setDebuggerFlag(newSid, true);

  if (otherProxy) {
    otherProxy->stop();
  }

  return true;
}
Beispiel #8
0
// Cleanup any proxies in our retired proxy queue. We'll pull a proxy
// out of the queue, ask it to cleanup which will wait for threads it
// owns to exit, then drop our shared reference to it. That may
// destroy the proxy here, or it may remain alive if there is a thread
// still processing an interrupt with it since such threads have their
// own shared reference.
void Debugger::cleanupRetiredProxies() {
  TRACE(7, "Debugger::cleanupRetiredProxies\n");
  DebuggerProxyPtr proxy;
  while (m_retiredProxyQueue.try_pop(proxy)) {
    try {
      // We give the proxy a short period of time to wait for any
      // threads it owns. If it doesn't succeed, we put it back and
      // try again later.
      TRACE(2, "Cleanup proxy %p\n", proxy.get());
      if (!proxy->cleanup(1)) {
        TRACE(2, "Proxy %p has not stopped yet\n", proxy.get());
        m_retiredProxyQueue.push(proxy);
      }
    } catch (Exception &e) {
      Logger::Error("Exception during proxy %p retirement: %s",
                    proxy.get(), e.getMessage().c_str());
    }
  }
}
void HphpdHook::onDefFunc(const Func* f) {
  // Make sure we have a proxy
  DebuggerProxyPtr proxy = Debugger::GetProxy();
  if (proxy == nullptr) return;

  // If the proxy has an enabled breakpoint that matches entry into the given
  // function, arrange for the VM to stop execution and notify the debugger
  // whenever execution enters the given function.
  std::vector<BreakPointInfoPtr> bps;
  proxy->getBreakPoints(bps);
  for (unsigned int i = 0; i < bps.size(); i++) {
    BreakPointInfoPtr bp = bps[i];
    if (bp->m_state == BreakPointInfo::Disabled) continue;
    if (!matchFunctionName(bp->getFuncName(), f)) continue;
    bp->m_bindState = BreakPointInfo::KnownToBeValid;
    phpAddBreakPointFuncEntry(f);
    return;
  }
}
Beispiel #10
0
void Debugger::switchSandbox(DebuggerProxyPtr proxy,
                             const std::string &newId) {
  WriteLock lock(m_mutex);

  string oldId = proxy->getSandboxId();
  m_proxies[oldId].reset();
  m_proxies[newId] = proxy;

  // makes sure proxy's sandbox info is complete with path, etc..
  if (m_sandboxes.find(newId) != m_sandboxes.end()) {
    proxy->updateSandbox(m_sandboxes[newId]);
  } else {
    DSandboxInfoPtr sb(new DSandboxInfo(newId));
    m_sandboxes[newId] = sb;
    proxy->updateSandbox(sb);
  }

  flagDebugger(newId);
}
Beispiel #11
0
void Debugger::registerSandbox(const DSandboxInfo &sandbox) {
  TRACE(2, "Debugger::registerSandbox\n");
  // update sandbox first
  updateSandbox(sandbox);

  // add thread do m_sandboxThreadInfoMap
  const StringData* sid = StringData::GetStaticString(sandbox.id());
  ThreadInfo* ti = ThreadInfo::s_threadInfo.getNoCheck();
  {
    SandboxThreadInfoMap::accessor acc;
    m_sandboxThreadInfoMap.insert(acc, sid);
    ThreadInfoSet& set = acc->second;
    set.insert(ti);
  }

  // find out whether this sandbox is being debugged
  DebuggerProxyPtr proxy = findProxy(sid);
  if (proxy) {
    ti->m_reqInjectionData.setDebugger(true);
    proxy->writeInjTablesToThread();
  }
}
Beispiel #12
0
bool Debugger::switchSandboxImpl(DebuggerProxyPtr proxy,
                                 const StringData* newSid,
                                 bool force) {
  TRACE(2, "Debugger::switchSandboxImpln");
  // Take the new sandbox
  DebuggerProxyPtr otherProxy;
  {
    ProxyMap::accessor acc;
    if (m_proxyMap.insert(acc, newSid)) {
      acc->second = proxy;
    } else {
      // the sandbox indicated by newId is already attached by another proxy
      if (!force) {
        return false;
      }
      // Delay the destruction of the proxy originally attached to the sandbox
      // to avoid calling a DebuggerProxy destructor (which can sleep) while
      // holding m_mutex.
      otherProxy = acc->second;
      acc->second = proxy;
    }
  }

  if (proxy->getSandbox().valid()) {
    // Detach from the old sandbox
    const StringData* oldSid =
      StringData::GetStaticString(proxy->getSandboxId());
    setDebuggerFlag(oldSid, false);
    m_proxyMap.erase(oldSid);
  }
  setDebuggerFlag(newSid, true);

  if (otherProxy) {
    otherProxy->forceQuit();
  }

  return true;
}
Beispiel #13
0
// Primary entrypoint for the debugger from the VM. Called in response to a host
// of VM events that the debugger is interested in. The debugger will execute
// any logic needed to handle the event, and will block below this to wait for
// and process more commands from the debugger client. This function will only
// return when the debugger is letting the thread continue execution, e.g., for
// flow control command like continue, next, etc.
void Debugger::Interrupt(int type, const char *program,
                         InterruptSite *site /* = NULL */,
                         const char *error /* = NULL */) {
  assert(RuntimeOption::EnableDebugger);
  TRACE_RB(2, "Debugger::Interrupt type %d\n", type);

  DebuggerProxyPtr proxy = GetProxy();
  if (proxy) {
    TRACE(3, "proxy != null\n");
    RequestInjectionData &rjdata = ThreadInfo::s_threadInfo->m_reqInjectionData;
    // The proxy will only service an interrupt if we've previously setup some
    // form of flow control command (steps, breakpoints, etc.) or if it's
    // an interrupt related to something like the session or request.
    if (proxy->needInterrupt() || type != BreakPointReached) {
      // Interrupts may execute some PHP code, causing another interruption.
      std::stack<void *> &interrupts = rjdata.interrupts;

      CmdInterrupt cmd((InterruptType)type, program, site, error);
      interrupts.push(&cmd);
      proxy->interrupt(cmd);
      interrupts.pop();
    }
    // Some cmds require us to interpret all instructions until the cmd
    // completes. Setting this will ensure we stay out of JIT code and in the
    // interpreter so phpDebuggerOpcodeHook has a chance to work.
    rjdata.setDebuggerIntr(proxy->needVMInterrupts());
  } else {
    TRACE(3, "proxy == null\n");
    // Debugger clients are disconnected abnormally, or this sandbox is not
    // being debugged.
    if (type == SessionStarted || type == SessionEnded) {
      // For command line programs, we need this exception to exit from
      // the infinite execution loop.
      throw DebuggerClientExitException();
    }
  }
}
Beispiel #14
0
void Debugger::Interrupt(int type, const char *program,
                         InterruptSite *site /* = NULL */,
                         const char *error /* = NULL */) {
  ASSERT(RuntimeOption::EnableDebugger);

  DebuggerProxyPtr proxy = GetProxy();
  if (proxy) {
    // Interrupts may execute some PHP code, causing another interruption.
    void *&tint = ThreadInfo::s_threadInfo->m_reqInjectionData.interrupt;
    if (!tint) {
      CmdInterrupt cmd((InterruptType)type, program, site, error);
      tint = &cmd;
      proxy->interrupt(cmd);
      tint = NULL;
    }
  } else {
    // debugger clients are disconnected abnormally
    if (type == SessionStarted || type == SessionEnded) {
      // for command line programs, we need this exception to exit from
      // the infinite execution loop
      throw DebuggerClientExitException();
    }
  }
}
Beispiel #15
0
// Place the proxy onto the retired proxy queue. It will be cleaned up
// and destroyed later by another thread.
void Debugger::retireProxy(DebuggerProxyPtr proxy) {
  TRACE(2, "Debugger::retireProxy %p\n", proxy.get());
  m_retiredProxyQueue.push(proxy);
}
Beispiel #16
0
void Debugger::removeProxy(DebuggerProxyPtr proxy) {
  WriteLock lock(m_mutex);
  m_proxies[proxy->getSandboxId()].reset();
}