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::PrintVariables(DebuggerClient &client, CArrRef variables, bool global, CStrRef text) { bool system = true; int i = 0; bool found = false; for (ArrayIter iter(variables); iter; ++iter) { String name = iter.first().toString(); String value = DebuggerClient::FormatVariable(iter.second(), 200); if (!text.empty()) { String fullvalue = DebuggerClient::FormatVariable(iter.second(), -1); if (name.find(text, 0, false) >= 0 || fullvalue.find(text, 0, false) >= 0) { client.print("%s = %s", name.data(), value.data()); found = true; } } else { if (global && system) { client.print("$%s = %s", name.data(), value.data()); } else { client.output("$%s = %s", name.data(), value.data()); } // we knew this is the last system global if (global && name == "http_response_header") { client.output("%s", ""); system = false; } ++i; if (i % DebuggerClient::ScrollBlockSize == 0 && client.ask("There are %zd more variables. Continue? [Y/n]", variables.size() - i) == 'n') { break; } } } if (!text.empty() && !found) { client.info("(unable to find specified text in any variables)"); } }
void CmdVariable::PrintVariables(DebuggerClient &client, CArrRef variables, int frame, const String& text, int version) { bool global = frame == -1; // I.e. we were called from CmdGlobal, or the //client's current frame is the global frame, according to OnServer bool system = true; int i = 0; bool found = false; for (ArrayIter iter(variables); iter; ++iter) { String name = iter.first().toString(); String value; if (version == 2) { // Using the new protocol, so variables contain only names. // Fetch the value separately. CmdVariable cmd; cmd.m_frame = frame; cmd.m_variables = null_array; cmd.m_varName = name; cmd.m_filter = text; cmd.m_version = 2; auto rcmd = client.xend<CmdVariable>(&cmd); if (!rcmd->m_variables.empty()) { value = DebuggerClient::FormatVariable(rcmd->m_variables[name], 200); found = true; } else if (text.empty()) { // Not missing because filtered out, assume the value is too large. value = s_omitted; found = true; } else { if (name.find(text, 0, false) >= 0) { // Server should have matched it. // Assume missing because value is too large. value = s_omitted; found = true; } else { // The variable was filtered out on the server, using text. // Or it was just too large. Either way we let skip over it. continue; } } } else { value = DebuggerClient::FormatVariable(iter.second(), 200); } if (version == 0 && !text.empty()) { if (name.find(text, 0, false) >= 0) { client.print("%s = %s", name.data(), value.data()); found = true; } else { String fullvalue = DebuggerClient::FormatVariable(value, -1); if (fullvalue.find(text, 0, false) >= 0) { client.print("%s = %s", name.data(), value.data()); found = true; } } } else { if (global && system) { client.print("$%s = %s", name.data(), value.data()); } else { client.output("$%s = %s", name.data(), value.data()); } // we know s_http_response_header is the last system global if (global && name == s_http_response_header) { client.output("%s", ""); system = false; } ++i; if (i % DebuggerClient::ScrollBlockSize == 0 && client.ask("There are %zd more variables. Continue? [Y/n]", variables.size() - i) == 'n') { break; } } } if (!text.empty() && !found) { client.info("(unable to find specified text in any variables)"); } }
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." ); } } }
void CmdVariable::PrintVariables(DebuggerClient &client, const Array& variables, int frame, const String& text, int version) { bool global = frame == -1; // I.e. we were called from CmdGlobal, or the //client's current frame is the global frame, according to OnServer bool system = true; int i = 0; bool found = false; always_assert(version == 2); for (ArrayIter iter(variables); iter; ++iter) { auto const name = iter.first().toString(); String value; // Using the new protocol, so variables contain only names. Fetch the value // separately. CmdVariable cmd(client.isStackTraceAsync() ? KindOfVariableAsync : KindOfVariable); cmd.m_frame = frame; cmd.m_variables.reset(); cmd.m_varName = name; cmd.m_filter = text; cmd.m_formatMaxLen = 200; cmd.m_version = 2; auto rcmd = client.xend<CmdVariable>(&cmd); if (!rcmd->m_variables.empty()) { assert(rcmd->m_variables[name].isString()); value = rcmd->m_variables[name].toString(); found = true; } else if (text.empty()) { // Not missing because filtered out, assume the value is too large. value = s_omitted; found = true; } else if (name.find(text, 0, false) >= 0) { // Server should have matched it. Assume missing because value is too // large. value = s_omitted; found = true; } else { // The variable was filtered out on the server, using text. Or it was // just too large. Either way we skip over it. continue; } if (global && system) { client.print("$%s = %s", name.data(), value.data()); } else { client.output("$%s = %s", name.data(), value.data()); } // We know s_HTTP_RAW_POST_DATA is the last system global. if (global && name == s_HTTP_RAW_POST_DATA) { client.output("%s", ""); system = false; } ++i; if (i % DebuggerClient::ScrollBlockSize == 0 && client.ask("There are %zd more variables. Continue? [Y/n]", variables.size() - i) == 'n') { break; } } if (!text.empty() && !found) { client.info("(unable to find specified text in any variables)"); } }
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. auto piece = folly::StringPiece{value.data(), static_cast<size_t>(charCount)}; client.output(piece); 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 = folly::StringPiece { value.data() + charCount, static_cast<size_t>(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."); } }