void CmdVariable::onClient(DebuggerClient &client) { if (DebuggerCommand::displayedHelp(client)) return; String text; if (client.argCount() == 1) { text = client.argValue(1); } else if (client.argCount() != 0) { help(client); return; } if (client.isStackTraceAsync()) { m_type = KindOfVariableAsync; } m_frame = client.getFrame(); auto cmd = client.xend<CmdVariable>(this); if (cmd->m_variables.empty()) { client.info("(no variable was defined)"); } else { PrintVariables(client, cmd->m_variables, cmd->m_global ? -1 : m_frame, text, cmd->m_version); } }
void CmdWhere::onClientImpl(DebuggerClient &client) { if (DebuggerCommand::displayedHelp(client)) return; if (client.argCount() > 1) { help(client); return; } Array st = fetchStackTrace(client); if (st.empty()) { client.info("(no stacktrace to display or in global scope)"); client.info("if you hit serialization limit, consider do " "\"set sa off\" and then get the stack without args"); return; } // so list command can default to current frame client.moveToFrame(client.getFrame(), false); if (client.argCount() == 0) { int i = 0; for (ArrayIter iter(st); iter; ++iter) { client.printFrame(i, iter.second().toArray()); ++i; if (!client.isApiMode() && i % DebuggerClient::ScrollBlockSize == 0 && client.ask("There are %zd more frames. Continue? [Y/n]", st.size() - i) == 'n') { break; } } } else { string snum = client.argValue(1); int num = atoi(snum.c_str()); if (snum[0] == '-') { snum = snum.substr(1); } if (!DebuggerClient::IsValidNumber(snum)) { client.error("The argument, if specified, has to be numeric."); return; } if (num > 0) { for (int i = 0; i < num && i < st.size(); i++) { client.printFrame(i, st[i].toArray()); } } else if (num < 0) { for (int i = st.size() + num; i < st.size(); i++) { client.printFrame(i, st[i].toArray()); } } else { client.error("0 was specified for the number of frames"); client.tutorial( "The optional argument is the number of frames to print out. " "Use a positive number to print out innermost frames. Use a negative " "number to print out outermost frames." ); } } }
void CmdVariable::PrintVariable(DebuggerClient &client, const String& varName) { CmdVariable cmd(client.isStackTraceAsync() ? KindOfVariableAsync : KindOfVariable); auto charCount = client.getDebuggerClientShortPrintCharCount(); cmd.m_frame = client.getFrame(); auto rcmd = client.xend<CmdVariable>(&cmd); always_assert(rcmd->m_version == 2); // Using the new protocol. rcmd contains a list of variables only. Fetch // value of varName only, so that we can recover nicely when its value is too // large to serialize. cmd.m_varName = varName; cmd.m_variables.reset(); cmd.m_formatMaxLen = charCount; cmd.m_version = 2; rcmd = client.xend<CmdVariable>(&cmd); if (rcmd->m_variables.empty()) { // Perhaps the value is too large? See recvImpl. Retry the command with // version 1, in which case values are omitted. cmd.m_version = 1; rcmd = client.xend<CmdVariable>(&cmd); if (!rcmd->m_variables.empty()) { // It's there without values, and gone with values, so it is too large. client.output(s_omitted); } return; } auto const get_var = [varName] (const CmdVariable& cmd) { assert(cmd.m_variables.size() == 1); assert(cmd.m_variables.exists(varName, true /* isKey */)); assert(cmd.m_variables[varName].isString()); return cmd.m_variables[varName].toString(); }; auto const value = get_var(*rcmd); if (charCount <= 0 || value.size() <= charCount) { client.output(value); return; } // Don't show the "omitted" suffix. client.output(StringSlice(value.data(), charCount)); if (client.ask("There are more characters. Continue? [y/N]") == 'y') { // Now we get the full value, and show the rest. cmd.m_variables.reset(); cmd.m_formatMaxLen = -1; rcmd = client.xend<CmdVariable>(&cmd); auto value = get_var(*rcmd); auto rest = StringSlice(value.data() + charCount, value.size() - charCount); client.output(rest); client.tutorial("You can use 'set cc n' to increase the character" " limit. 'set cc -1' will remove the limit."); } }
void CmdEval::onClient(DebuggerClient &client) { m_body = client.getCode(); m_frame = client.getFrame(); m_bypassAccessCheck = client.getDebuggerClientBypassCheck(); auto res = client.xendWithNestedExecution<CmdEval>(this); res->handleReply(client); m_failed = res->m_failed; }
void CmdPrint::onClientImpl(DebuggerClient &client) { if (DebuggerCommand::displayedHelp(client)) return; if (client.argCount() == 0) { help(client); return; } int index = 1; if (client.arg(1, "always")) { m_isForWatch = true; if (client.argCount() == 1) { client.error("'[p]rint [a]lways' needs an expression to watch."); return; } index++; } else if (client.arg(1, "list")) { m_isForWatch = true; processList(client); return; } else if (client.arg(1, "clear")) { m_isForWatch = true; processClear(client); return; } const char *format = nullptr; for (const char **fmt = Formats; *fmt; fmt++) { if (client.arg(index, *fmt)) { format = *fmt; index++; break; } } m_body = client.lineRest(index); if (m_isForWatch) { client.addWatch(format, m_body); return; } m_bypassAccessCheck = client.getDebuggerBypassCheck(); m_printLevel = client.getDebuggerPrintLevel(); assert(m_printLevel <= 0 || m_printLevel >= DebuggerClient::MinPrintLevel); m_frame = client.getFrame(); CmdPrintPtr res = client.xend<CmdPrint>(this); if (!res->is(m_type)) { assert(client.isApiMode()); m_incomplete = true; res->setClientOutput(client); } else { m_output = res->m_output; m_ret = res->m_ret; if (!m_output.empty()) { client.output(m_output); } client.output(FormatResult(format, m_ret)); } }
void CmdEval::onClient(DebuggerClient &client) { m_body = client.getCode(); m_frame = client.getFrame(); m_bypassAccessCheck = client.getDebuggerClientBypassCheck(); auto res = client.xendWithNestedExecution<CmdEval>(this); assertx(res->is(DebuggerCommand::KindOfEval)); auto eval = std::static_pointer_cast<CmdEval>(res); eval->handleReply(client); m_failed = eval->m_failed; }
Variant CmdPrint::processWatch(DebuggerClient &client, const char *format, const std::string &php) { m_body = php; m_frame = client.getFrame(); m_noBreak = true; CmdPrintPtr res = client.xend<CmdPrint>(this); if (!res->m_output.empty()) { client.output(res->m_output); } return res->m_ret; }
void CmdVariable::PrintVariable(DebuggerClient &client, CStrRef varName) { CmdVariable cmd; cmd.m_frame = client.getFrame(); CmdVariablePtr rcmd = client.xend<CmdVariable>(&cmd); if (!rcmd->m_variables.empty()) { for (ArrayIter iter(rcmd->m_variables); iter; ++iter) { String name = iter.first().toString(); if (!name.equal(varName)) continue; String value = DebuggerClient::FormatVariable(iter.second(), 200); client.print("%s", value.data()); } } }
void CmdEval::onClientImpl(DebuggerClient &client) { m_body = client.getCode(); m_frame = client.getFrame(); m_bypassAccessCheck = client.getDebuggerBypassCheck(); DebuggerCommandPtr res = client.xendWithNestedExecution<DebuggerCommand>(this); if (!res->is(m_type)) { assert(client.isApiMode()); m_incomplete = true; res->setClientOutput(client); } else { res->handleReply(client); } }
void CmdDown::onClient(DebuggerClient &client) { if (DebuggerCommand::displayedHelp(client)) return; if (client.argCount() > 1) { help(client); } else { if (client.isStackTraceAsync()) { CmdWhere(KindOfWhereAsync).fetchStackTrace(client); } else { CmdWhere().fetchStackTrace(client); } client.moveToFrame(client.getFrame() - CmdUp::ParseNumber(client)); } }
void CmdEval::onClientImpl(DebuggerClient &client) { m_body = client.getCode(); m_frame = client.getFrame(); m_bypassAccessCheck = client.getDebuggerBypassCheck(); client.sendToServer(this); DebuggerCommandPtr res = client.recvFromServer(m_type); if (!res->is(m_type)) { assert(client.isApiMode()); m_incomplete = true; res->setClientOutput(client); } else { res->handleReply(client); } }
void CmdVariable::PrintVariable(DebuggerClient &client, const String& varName) { CmdVariable cmd(client.isStackTraceAsync() ? KindOfVariableAsync : KindOfVariable); auto charCount = client.getDebuggerClientShortPrintCharCount(); cmd.m_frame = client.getFrame(); auto rcmd = client.xend<CmdVariable>(&cmd); if (rcmd->m_version == 2) { // Using the new protocol. rcmd contains a list of variables only. // Fetch value of varName only, so that we can recover nicely when its // value is too large to serialize. cmd.m_varName = varName; cmd.m_variables.reset(); cmd.m_version = 2; rcmd = client.xend<CmdVariable>(&cmd); if (rcmd->m_variables.empty()) { // Perhaps the value is too large? See recvImpl. // Retry the command with version 1, in which case values are omitted. cmd.m_version = 1; rcmd = client.xend<CmdVariable>(&cmd); if (!rcmd->m_variables.empty()) { // It's there without values, and gone with values, so it is too large. client.output("...(omitted)"); return; } } } if (!rcmd->m_variables.empty()) { for (ArrayIter iter(rcmd->m_variables); iter; ++iter) { String name = iter.first().toString(); if (!name.equal(varName)) continue; String value = DebuggerClient::FormatVariable(iter.second(), -1); auto excess = value.length() - charCount; if (charCount <= 0 || excess <= 0) { client.output("%s", value.data()); } else { client.output("%s", value.substr(0, charCount).data()); if (client.ask("There are %d more characters. Continue? [y/N]", excess) == 'y') { client.output("%s", value.substr(charCount).data()); client.tutorial("You can use 'set cc n' to increase the character" " limit. 'set cc -1' will remove the limit."); } } } } }
void CmdVariable::onClient(DebuggerClient &client) { if (DebuggerCommand::displayedHelp(client)) return; String text; if (client.argCount() == 1) { text = client.argValue(1); } else if (client.argCount() != 0) { help(client); return; } m_frame = client.getFrame(); CmdVariablePtr cmd = client.xend<CmdVariable>(this); if (cmd->m_variables.empty()) { client.info("(no variable was defined)"); } else { m_variables = cmd->m_variables; PrintVariables(client, cmd->m_variables, cmd->m_global, text); } }
void CmdWhere::onClient(DebuggerClient &client) { if (DebuggerCommand::displayedHelp(client)) return; if (client.argCount() > 2) { help(client); return; } int argBase = 1; if ((client.argCount() > 0) && client.arg(argBase, "async")) { // We use a different command type for an async stack trace, so we // can both send and receive different data and still keep the // existing Where command unchanged. This ensures that old clients // can still get a stack trace from a newer server, and vice // versa. m_type = KindOfWhereAsync; argBase++; client.info("Fetching async stacktrace..."); } Array st = fetchStackTrace(client); if (st.empty()) { if (m_type != KindOfWhereAsync) { client.info("(no stacktrace to display or in global scope)"); client.info("If you hit the serialization limit, try " "\"set sa off\" to get the stack without args"); } else { client.info("(no async stacktrace to display)"); } return; } // so list command can default to current frame client.moveToFrame(client.getFrame(), false); if (client.argCount() < argBase) { int i = 0; for (ArrayIter iter(st); iter; ++iter) { client.printFrame(i, iter.second().toArray()); ++i; if (i % DebuggerClient::ScrollBlockSize == 0 && client.ask("There are %zd more frames. Continue? [Y/n]", st.size() - i) == 'n') { break; } } } else { std::string snum = client.argValue(argBase); int num = atoi(snum.c_str()); if (snum[0] == '-') { snum = snum.substr(1); } if (!DebuggerClient::IsValidNumber(snum)) { client.error("The argument, if specified, has to be numeric."); return; } if (num > 0) { for (int i = 0; i < num && i < st.size(); i++) { client.printFrame(i, st[i].toArray()); } } else if (num < 0) { for (int i = st.size() + num; i < st.size(); i++) { client.printFrame(i, st[i].toArray()); } } else { client.error("0 was specified for the number of frames"); client.tutorial( "The optional argument is the number of frames to print out. " "Use a positive number to print out innermost frames. Use a negative " "number to print out outermost frames." ); } } }