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 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."); } }
Array CmdWhere::fetchStackTrace(DebuggerClient &client) { Array st = client.getStackTrace(); // Only grab a new stack trace if we don't have one cached, or if // the one cached does not match the type of stack trace being // requested. bool isAsync = m_type == KindOfWhereAsync; if (st.isNull() || (isAsync != client.isStackTraceAsync())) { m_stackArgs = client.getDebuggerClientStackArgs(); auto cmd = client.xend<CmdWhere>(this); st = cmd->m_stacktrace; client.setStackTrace(st, isAsync); } return st; }
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 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, 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; 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(client.isStackTraceAsync() ? KindOfVariableAsync : KindOfVariable); 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_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)"); } }