bool CmdMachine::onServer(DebuggerProxy &proxy) { if (m_body == "rpc") { String host = m_rpcConfig[s_host].toString(); if (host.empty()) { register_intercept("", false, uninit_null()); } else { int port = m_rpcConfig[s_port].toInt32(); LibEventHttpClient::SetCache(host.data(), port, 1); register_intercept("", "fb_rpc_intercept_handler", m_rpcConfig); } return proxy.sendToClient(this); } if (m_body == "list") { Debugger::GetRegisteredSandboxes(m_sandboxes); return proxy.sendToClient(this); } if (m_body == "attach" && !m_sandboxes.empty()) { m_succeed = proxy.switchSandbox(m_sandboxes[0]->id(), m_force); if (m_succeed) { proxy.notifyDummySandbox(); m_exitInterrupt = true; } return proxy.sendToClient(this); } return false; }
bool CmdWhere::onServer(DebuggerProxy &proxy) { m_stacktrace = g_vmContext->debugBacktrace(false, true, false); if (!m_stackArgs) { processStackTrace(); } return proxy.sendToClient(this); }
bool CmdVariable::onServer(DebuggerProxy &proxy) { if (m_type == KindOfVariableAsync) { //we only do variable inspection on continuation wait handles auto frame = getWaitHandleAtAsyncStackPosition(m_frame); if (frame != nullptr) { auto fp = frame->actRec(); if (fp != nullptr) { m_variables = getVariables(fp); } } } else if (m_frame < 0) { m_variables = g_context->m_globalVarEnv->getDefinedVariables(); m_global = true; } else { m_variables = g_context->getLocalDefinedVariables(m_frame); m_global = g_context->getVarEnv(m_frame) == g_context->m_globalVarEnv; } if (m_global) { m_variables.remove(s_GLOBALS); } if (m_version == 1) { // Remove the values before sending to client. ArrayInit ret(m_variables->size(), ArrayInit::Map{}); Variant v; for (ArrayIter iter(m_variables); iter; ++iter) { ret.add(iter.first().toString(), v); } m_variables = ret.toArray(); m_version = 2; } else if (m_version == 2) { // Remove entries that do not match a non empty m_varName. if (!m_varName.empty()) { ArrayInit ret(1, ArrayInit::Map{}); ret.add(m_varName, m_variables[m_varName]); m_variables = ret.toArray(); } // Remove entries whose name or contents do not match a non empty m_filter if (!m_filter.empty()) { ArrayInit ret(m_variables.size(), ArrayInit::Map{}); for (ArrayIter iter(m_variables); iter; ++iter) { String name = iter.first().toString(); if (name.find(m_filter, 0, false) < 0) { String fullvalue = DebuggerClient::FormatVariable(iter.second(), -1); if (fullvalue.find(m_filter, 0, false) < 0) { continue; } } ret.add(name, iter.second()); } m_variables = ret.toArray(); } } return proxy.sendToClient(this); }
bool CmdThread::onServer(DebuggerProxy &proxy) { if (m_body == "info") { // collect info InfoVec info; debuggerInfo(info); Transport *transport = g_context->getTransport(); if (transport) { transport->debuggerInfo(info); } else { Add(info, "Thread Type", proxy.getThreadType()); } g_context->debuggerInfo(info); m_out = DebuggerClient::FormatInfoVec(info); return proxy.sendToClient(this); } if (m_body == "list") { proxy.getThreads(m_threads); return proxy.sendToClient(this); } if (m_body == "switch") { if (!m_threads.empty()) { proxy.switchThread(m_threads[0]); m_exitInterrupt = true; return true; } } if (m_body == "normal") { proxy.switchThreadMode(DebuggerProxy::Normal); return true; } if (m_body == "sticky") { proxy.switchThreadMode(DebuggerProxy::Sticky); return true; } if (m_body == "exclusive") { proxy.switchThreadMode(DebuggerProxy::Exclusive); return true; } assert(false); return false; }
bool CmdWhere::onServer(DebuggerProxy &proxy) { if (m_type == KindOfWhereAsync) { m_stacktrace = createAsyncStacktrace(); } else { m_stacktrace = createBacktrace(BacktraceArgs() .withSelf() .ignoreArgs(!m_stackArgs)); } return proxy.sendToClient(this); }
bool CmdEval::onServer(DebuggerProxy &proxy) { PCFilter* locSave = g_vmContext->m_lastLocFilter; g_vmContext->m_lastLocFilter = new PCFilter(); g_vmContext->setDebuggerBypassCheck(m_bypassAccessCheck); DebuggerProxy::ExecutePHP(m_body, m_output, !proxy.isLocal(), m_frame); g_vmContext->setDebuggerBypassCheck(false); delete g_vmContext->m_lastLocFilter; g_vmContext->m_lastLocFilter = locSave; return proxy.sendToClient(this); }
bool CmdInstrument::onServer(DebuggerProxy &proxy) { m_instPoints = &m_ips; m_enabled = true; if (m_type == ActionRead) { readFromTable(proxy); } else if (m_type == ActionWrite) { validateAndWriteToTable(proxy); } return proxy.sendToClient(this); }
bool CmdShell::onServer(DebuggerProxy &proxy) { const char **argv = (const char **)malloc((m_args.size() + 1) * sizeof(char*)); for (unsigned int i = 0; i < m_args.size(); i++) { argv[i] = (char*)m_args[i].c_str(); } argv[m_args.size()] = nullptr; Process::Exec(argv[0], argv, nullptr, m_out, &m_out, true); free(argv); return proxy.sendToClient(this); }
bool CmdWhere::onServer(DebuggerProxy &proxy) { if (m_type == KindOfWhereAsync) { m_stacktrace = createAsyncStacktrace(); } else { m_stacktrace = g_vmContext->debugBacktrace(false, true, false); if (!m_stackArgs) { removeArgs(); } } return proxy.sendToClient(this); }
bool CmdInternalTesting::onServer(DebuggerProxy &proxy) { TRACE(2, "CmdInternalTesting::onServer\n"); if (m_arg == "badcmdtypereceive") { m_type = KindOfInternalTestingBad; // Send back a bad cmd. } else if (m_arg == "shortcmdreceive") { m_arg = "shortcmd"; // Force send to drop a field } else if (m_arg == "segfaultServer") { int *px = nullptr; *px = 42; } return proxy.sendToClient(this); }
bool CmdGlobal::onServer(DebuggerProxy &proxy) { m_globals = CmdVariable::GetGlobalVariables(); if (m_version == 2) { // Remove the values before sending to client. ArrayInit ret(m_globals->size(), ArrayInit::Map{}); Variant v; for (ArrayIter iter(m_globals); iter; ++iter) { ret.add(iter.first().toString(), v); } m_globals = ret.toArray(); } return proxy.sendToClient(this); }
// NB: unlike most other commands, the client expects that more interrupts // can occur while we're doing the server-side work for an eval. bool CmdEval::onServer(DebuggerProxy &proxy) { PCFilter* locSave = g_vmContext->m_lastLocFilter; g_vmContext->m_lastLocFilter = new PCFilter(); g_vmContext->setDebuggerBypassCheck(m_bypassAccessCheck); proxy.ExecutePHP(m_body, m_output, m_frame, m_failed, DebuggerProxy::ExecutePHPFlagsAtInterrupt | (!proxy.isLocal() ? DebuggerProxy::ExecutePHPFlagsLog : DebuggerProxy::ExecutePHPFlagsNone)); g_vmContext->setDebuggerBypassCheck(false); delete g_vmContext->m_lastLocFilter; g_vmContext->m_lastLocFilter = locSave; return proxy.sendToClient(this); }
// NB: unlike most other commands, the client expects that more interrupts // can occur while we're doing the server-side work for an eval. bool CmdEval::onServer(DebuggerProxy &proxy) { PCFilter locSave; RequestInjectionData &rid = ThreadInfo::s_threadInfo->m_reqInjectionData; locSave.swap(rid.m_flowFilter); g_context->debuggerSettings.bypassCheck = m_bypassAccessCheck; proxy.ExecutePHP(m_body, m_output, m_frame, m_failed, DebuggerProxy::ExecutePHPFlagsAtInterrupt | (!proxy.isLocal() ? DebuggerProxy::ExecutePHPFlagsLog : DebuggerProxy::ExecutePHPFlagsNone)); g_context->debuggerSettings.bypassCheck = false; locSave.swap(rid.m_flowFilter); return proxy.sendToClient(this); }
bool CmdPrint::onServer(DebuggerProxy &proxy) { PCFilter* locSave = g_vmContext->m_lastLocFilter; g_vmContext->m_lastLocFilter = new PCFilter(); g_vmContext->setDebuggerBypassCheck(m_bypassAccessCheck); { EvalBreakControl eval(m_noBreak); m_ret = DebuggerProxy::ExecutePHP(DebuggerProxy::MakePHPReturn(m_body), m_output, !proxy.isLocal(), m_frame); } g_vmContext->setDebuggerBypassCheck(false); delete g_vmContext->m_lastLocFilter; g_vmContext->m_lastLocFilter = locSave; return proxy.sendToClient(this); }
bool CmdVariable::onServer(DebuggerProxy &proxy) { if (m_frame < 0) { m_variables = g_vmContext->m_globalVarEnv->getDefinedVariables(); m_global = true; } else { m_variables = g_vmContext->getLocalDefinedVariables(m_frame); m_global = g_vmContext->getVarEnv(m_frame) == g_vmContext->m_globalVarEnv; } if (m_global) { m_variables.remove(s_GLOBALS); } if (m_version == 1) { // Remove the values before sending to client. ArrayInit ret(m_variables->size()); Variant v; for (ArrayIter iter(m_variables); iter; ++iter) { ret.add(iter.first().toString(), v); } m_variables = ret.toArray(); m_version = 2; } else if (m_version == 2) { // Remove entries that do not match a non empty m_varName. if (!m_varName.empty()) { ArrayInit ret(1); ret.add(m_varName, m_variables[m_varName]); m_variables = ret.toArray(); } // Remove entries whose name or contents do not match a non empty m_filter if (!m_filter.empty()) { ArrayInit ret(1); for (ArrayIter iter(m_variables); iter; ++iter) { String name = iter.first().toString(); if (name.find(m_filter, 0, false) < 0) { String fullvalue = DebuggerClient::FormatVariable(iter.second(), -1); if (fullvalue.find(m_filter, 0, false) < 0) { continue; } } ret.add(name, iter.second()); } m_variables = ret.toArray(); } } return proxy.sendToClient(this); }
bool CmdExtension::onServer(DebuggerProxy &proxy) { if (m_args.size() <= 1) { return processList(proxy); } std::string name = m_args[1]; Extension *ext = Extension::GetExtension(name); if (ext) { if (m_args.size() == 2) { if (ext->debuggerSupport() & IDebuggable::SupportInfo) { IDebuggable::InfoVec info; ext->debuggerInfo(info); m_out = DebuggerClient::FormatInfoVec(info); } else { m_err = "Extension doesn't have summary information."; } } else if (DebuggerClient::Match(m_args[2].c_str(), "dump")) { if (ext->debuggerSupport() & IDebuggable::SupportDump) { m_out = ext->debuggerDump(); m_out += "\n"; } else { m_err = "Extension doesn't have detailed dumps."; } } else { if (ext->debuggerSupport() & IDebuggable::SupportVerb) { std::string verb = m_args[2]; std::vector<std::string> args; if (m_args.size() > 3) { args.insert(args.end(), m_args.begin() + 3, m_args.end()); } m_out = ext->debuggerVerb(verb, args); m_out += "\n"; } else { m_err = "Extension doesn't support any debugger actions."; } } } else { m_err = "Unable to find the specified extension: "; m_err += String(name); } return proxy.sendToClient(this); }
// Tries to read the contents of the file whose path is specified in m_file. // If the path cannot be resolved and is relative, the path of the sandbox // is used to qualify the relative path. If the contents cannot be retrieved // m_code will be an empty string. // The function returns false if the reply to the client fails during the // sending process. bool CmdList::onServer(DebuggerProxy &proxy) { auto savedWarningFrequency = RuntimeOption::WarningFrequency; RuntimeOption::WarningFrequency = 0; m_code = f_file_get_contents(m_file.c_str()); if (!proxy.isLocal() && !m_code.toBoolean() && m_file[0] != '/') { DSandboxInfo info = proxy.getSandbox(); if (info.m_path.empty()) { raise_warning("path for sandbox %s is not setup, run a web request", info.desc().c_str()); } else { std::string full_path = info.m_path + m_file; m_code = f_file_get_contents(full_path.c_str()); } } RuntimeOption::WarningFrequency = savedWarningFrequency; if (!m_code.toBoolean() && m_file == "systemlib.php") { m_code = SystemLib::s_source; } return proxy.sendToClient((DebuggerCommand*)this); }
bool CmdExtension::processList(DebuggerProxy &proxy) { IDebuggable::InfoVec info; Array exts = Extension::GetLoadedExtensions(); typedef std::set<std::string, string_lessi> sorted_iset; sorted_iset names; for (ArrayIter iter(exts); iter; ++iter) { names.insert(iter.second().toString().data()); } for (sorted_iset::const_iterator iter = names.begin(); iter != names.end(); ++iter) { Extension *ext = Extension::GetExtension(*iter); assert(ext); if (ext) { int support = ext->debuggerSupport(); std::string line; line += (support & IDebuggable::SupportInfo) ? "Yes " : " "; line += (support & IDebuggable::SupportDump) ? "Yes " : " "; line += (support & IDebuggable::SupportVerb) ? "Yes " : " "; line += ext->getVersion(); IDebuggable::Add(info, iter->c_str(), line); } } int nameLen; String body = DebuggerClient::FormatInfoVec(info, &nameLen); int hrLen = nameLen + 42; if (hrLen > DebuggerClient::LineWidth) hrLen = DebuggerClient::LineWidth; StringBuffer sb; for (int i = 0; i < hrLen; i++) sb.append(BOX_H); sb.append("\n"); sb.append(StringUtil::Pad("Name\\Support", nameLen)); sb.append("Info Dump Verb Version\n"); for (int i = 0; i < hrLen; i++) sb.append(BOX_H); sb.append("\n"); sb.append(body); for (int i = 0; i < hrLen; i++) sb.append(BOX_H); sb.append("\n"); m_out = sb.detach(); return proxy.sendToClient(this); }
bool CmdHeaptrace::onServer(DebuggerProxy &proxy) { // globals std::vector<TypedValue *> roots; CArrRef arr = g_vmContext->m_globalVarEnv->getDefinedVariables(); arr->getChildren(roots); // static properties for (AllClasses ac; !ac.empty();) { Class *c = ac.popFront(); c->getChildren(roots); } // locals int numFrames = proxy.getRealStackDepth(); std::vector<Array> locs; for (int i = 0; i < numFrames; ++i) { locs.push_back(g_vmContext->getLocalDefinedVariables(i)); } for (CArrRef locArr : locs) { locArr->getChildren(roots); } Tracer<Accum>::traceAll( roots, [](TypedValue *node, Accum &accum) { accum.typesMap[(int64_t)node] = (int8_t)node->m_type; accum.sizeMap[(int64_t)node] = (int64_t)MemoryProfile::getSizeOfTV(node); }, [](TypedValue *parent, TypedValue *child, Accum &accum) { accum.adjacencyList[(int64_t)parent].push_back((int64_t)child); }, m_accum ); return proxy.sendToClient(this); }
bool CmdInterrupt::onServer(DebuggerProxy &proxy) { return proxy.sendToClient(this); }
// Sends an acknowledgment back to the client so that // it can go ahead and terminate. It it were to do so // before the server has received the command, the pipe // will be closed and the proxy will carry on as if there // were a communication failure, which is not as clean // explicitly quitting. bool CmdQuit::onServer(DebuggerProxy &proxy) { TRACE(2, "CmdQuit::onServer\n"); return proxy.sendToClient(this); }
bool CmdVariable::onServer(DebuggerProxy &proxy) { m_variables = g_vmContext->getLocalDefinedVariables(m_frame); return proxy.sendToClient(this); }
bool CmdVariable::onServer(DebuggerProxy &proxy) { if (m_type == KindOfVariableAsync) { //we only do variable inspection on continuation wait handles auto frame = getWaitHandleAtAsyncStackPosition(m_frame); if (frame != nullptr) { auto fp = frame->actRec(); if (fp != nullptr) { m_variables = getDefinedVariables(fp); } } } else if (m_frame < 0) { m_variables = g_context->m_globalVarEnv->getDefinedVariables(); m_global = true; } else { m_variables = g_context->getLocalDefinedVariables(m_frame); m_global = g_context->hasVarEnv(m_frame) == g_context->m_globalVarEnv; auto oThis = g_context->getThis(); if (nullptr != oThis) { TypedValue tvThis; tvThis.m_type = KindOfObject; tvThis.m_data.pobj = oThis; Variant thisName(s_this); m_variables.add(thisName, tvAsVariant(&tvThis)); } } if (m_global) { m_variables.remove(s_GLOBALS); } // Deprecated IDE uses this, so keep it around for now. It expects all // variable values to be fully serialized across the wire. if (m_version == 0) { return proxy.sendToClient(this); } // Version 1 of this command means we want the names of all variables, but we // don't care about their values just yet. if (m_version == 1) { ArrayInit ret(m_variables->size(), ArrayInit::Map{}); Variant v; for (ArrayIter iter(m_variables); iter; ++iter) { assert(iter.first().isString()); ret.add(iter.first().toString(), v); } m_variables = ret.toArray(); m_version = 2; return proxy.sendToClient(this); } // Version 2 of this command means we're trying to get the value of a single // variable. always_assert(m_version == 2); always_assert(!m_varName.empty()); // Variable name might not exist. if (!m_variables.exists(m_varName, true /* isKey */)) { m_variables = Array::Create(); return proxy.sendToClient(this); } auto const value = m_variables[m_varName]; auto const result = m_formatMaxLen < 0 ? DebuggerClient::FormatVariable(value) : DebuggerClient::FormatVariableWithLimit(value, m_formatMaxLen); m_variables = Array::Create(m_varName, result); // Remove the entry if its name or context does not match the filter. if (!m_filter.empty() && m_varName.find(m_filter, 0, false) < 0) { auto const fullvalue = DebuggerClient::FormatVariable(value); if (fullvalue.find(m_filter, 0, false) < 0) { m_variables = Array::Create(); } } return proxy.sendToClient(this); }
bool CmdSignal::onServer(DebuggerProxy &proxy) { return proxy.sendToClient(this); }