void CmdZend::onClientImpl(DebuggerClient &client) { if (DebuggerCommand::displayedHelp(client)) return; if (client.argCount() == 0) { const std::string &code = client.getCode(); if (!code.empty()) { const std::string zendExe = client.getZendExecutable(); client.info("Executing last PHP block with \"%s\"...", zendExe.c_str()); string out; Process::Exec(zendExe.c_str(), nullptr, code.c_str(), out, &out, true); client.print(out); return; } } help(client); }
void CmdHelp::onClientImpl(DebuggerClient &client) { if (DebuggerCommand::displayedHelp(client)) return; if (client.argCount() == 0) { HelpAll(client); } else if (client.arg(1, "start")) { HelpStarted(client); } else if (client.arg(1, "tutorial")) { if (!processTutorial(client)) { help(client); } } else { client.swapHelp(); if (!client.process()) { help(client); } } }
void CmdMacro::onClientImpl(DebuggerClient &client) { if (DebuggerCommand::displayedHelp(client)) return; if (client.argCount() == 0) { help(client); return; } if (client.arg(1, "start")) { client.startMacro(client.argValue(2)); } else if (client.arg(1, "end")) { client.endMacro(); } else if (client.arg(1, "replay")) { if (!client.playMacro(client.argValue(2))) { client.error("Unable to find specified macro."); processList(client); } } else if (client.arg(1, "list")) { processList(client); } else if (client.arg(1, "clear")) { string snum = client.argValue(2); if (!DebuggerClient::IsValidNumber(snum)) { client.error("'& [c]lear' needs an {index} argument."); client.tutorial( "You will have to run '& [l]ist' first to see a list of valid " "numbers or indices to specify." ); return; } int num = atoi(snum.c_str()); if (!client.deleteMacro(num)) { client.error("\"%s\" is not a valid macro index. Choose one from " "this list:", snum.c_str()); processList(client); return; } } }
// Sends an Info command to the server to retrieve source location // information for the function or class specified by the command // argument. Then updates this command with the source information // and sends it to the server in order to retrieve the source // text from the server. // Returns false if the server was unable to return the information // needed for this command. bool CmdList::listFunctionOrClass(DebuggerClient &client) { assert(client.argCount() == 1); auto cmdInfo = std::make_shared<CmdInfo>(); DebuggerCommandPtr deleter(cmdInfo); std::string subsymbol; cmdInfo->parseOneArg(client, subsymbol); auto cmd = client.xend<CmdInfo>(cmdInfo.get()); Array info = cmd->getInfo(); if (info.empty()) return false; always_assert(info.size() == 1); ArrayIter iter(info); Array funcInfo = iter.second().toArray(); if (!subsymbol.empty()) { String key = CmdInfo::FindSubSymbol(funcInfo[s_methods].toArray(), subsymbol); if (key.isNull()) return false; funcInfo = funcInfo[s_methods].toArray()[key].toArray(); } String file = funcInfo[s_file].toString(); int line1 = funcInfo[s_line1].toInt32(); int line2 = funcInfo[s_line2].toInt32(); int line = line1 ? line1 : line2; if (file.empty() || !line) return false; client.setListLocation(file.data(), line - 1, false); line = 0; int charFocus0 = 0; int lineFocus1 = 0; int charFocus1 = 0; m_file.clear(); m_line1 = m_line2 = 0; getListLocation(client, line, charFocus0, lineFocus1, charFocus1); if (m_file.empty()) { listEvalCode(client); return true; } return listFileRange(client, line, charFocus0, lineFocus1, charFocus1); }
void CmdThread::onClient(DebuggerClient &client) { if (DebuggerCommand::displayedHelp(client)) return; if (client.argCount() > 1) { help(client); return; } if (client.argCount() == 0) { m_body = "info"; auto res = client.xend<CmdThread>(this); client.print(res->m_out); } else if (client.arg(1, "list")) { processList(client); } else if (client.arg(1, "normal")) { m_body = "normal"; client.sendToServer(this); client.info("Thread is running in normal mode now. Other threads will " "interleave when they hit breakpoints as well."); } else if (client.arg(1, "sticky")) { m_body = "sticky"; client.sendToServer(this); client.info("Thread is running in sticky mode now. All other threads " "will wait until this thread finishes, when they hit " "breakpoints."); } else if (client.arg(1, "exclusive")) { m_body = "exclusive"; client.sendToServer(this); client.info("Thread is running in exclusive mode now. All other threads " "will not break, even when they hit breakpoints."); } else { std::string snum = client.argValue(1); if (!DebuggerClient::IsValidNumber(snum)) { client.error("'[t]hread {index}' needs a numeric argument."); client.tutorial( "You will have to run '[t]hread [l]ist' first to see a list of valid " "numbers or indices to specify. Thread 1 is always your current " "thread. If that's the only thread on the list, you do not have " "another thread at break to switch to." ); return; } int num = atoi(snum.c_str()); DThreadInfoPtr thread = client.getThread(num); if (!thread) { processList(client, false); thread = client.getThread(num); if (!thread) { client.error("\"%s\" is not a valid thread index. Choose one from " "this list:", snum.c_str()); processList(client); return; } } if (thread->m_id == client.getCurrentThreadId()) { client.info("This is your current thread already."); return; } m_body = "switch"; m_threads.push_back(thread); client.sendToServer(this); throw DebuggerConsoleExitException(); } }
void CmdConfig::onClient(DebuggerClient &client) { if (DebuggerCommand::displayedHelp(client)) return; if (client.argCount() == 0) { listVars(client); return; } std::string var = client.argValue(1); if (var == "help" || client.argCount() < 2) { help(client); return; } std::string value = client.argValue(2); if (var == "BypassAccessCheck" || var == "bac") { if (value == "on") { client.print("BypassAccessCheck(bac) set to on.\n" "All code executed from debugger is bypassing " "access check!"); client.setDebuggerClientBypassCheck(true); } else if (value == "off") { client.print("BypassAccessCheck(bac) set to off"); client.setDebuggerClientBypassCheck(false); } else { help(client); } return; } if (var == "LogFile" || var == "lf") { // Close the current log file handler FILE *f = client.getLogFileHandler(); if (f != nullptr) { fclose(f); client.setLogFileHandler(nullptr); } if (value == "off") { value = ""; } else { // Try open the log file and error if it's not working f = fopen(value.c_str(), "a"); if (f == nullptr) { client.error("Cannot open log file '%s'", value.c_str()); value = ""; client.setLogFileHandler(nullptr); } else { client.setLogFileHandler(f); } } client.print("LogFile(lf) is set to %s", value == "" ? "off" : value.c_str()); client.setLogFile(value); return; } if (var == "PrintLevel" || var == "pl") { int pl = strtol(value.c_str(), nullptr, 10); if (pl > 0 && pl < DebuggerClient::MinPrintLevel) { client.error("%d is invalid for PrintLevel(pl)", pl); return; } client.setDebuggerClientPrintLevel(pl); client.print("PrintLevel(pl) is set to %d", pl); return; } if (var == "ShortPrintCharCount" || var == "cc") { int cc = strtol(value.c_str(), nullptr, 10); if (cc < -1) { client.error("%d is invalid for ShortPrintCharCount(cc)", cc); } else { client.setDebuggerClientShortPrintCharCount(cc); client.print("ShortPrintCharCount(cc) is set to %d", cc); } return; } if (var == "SmallStep" || var == "ss") { if (value == "on") { client.print("SmallStep(ss) set to on.\n"); client.setDebuggerClientSmallStep(true); } else if (value == "off") { client.print("SmallStep(ss) set to off"); client.setDebuggerClientSmallStep(false); } else { help(client); } return; } if (var == "StackArgs" || var == "sa") { if (value == "on") { client.print("StackArgs(sa) set to on.\n"); client.setDebuggerClientStackArgs(true); } else if (value == "off") { client.print("StackArgs(sa) set to off"); client.setDebuggerClientStackArgs(false); } else { help(client); } return; } if (var == "MaxCodeLines" || var == "mcl") { // MaxCodeLines: a useful configuration variable for emacs/hphpd-integration // to prevent or limit code spew after each breakpoint is hit (since emacs // hphpd-mode already loads the source file into a buffer and displays a // pointer to the current line). int mcl = strtol(value.c_str(), nullptr, 10); if (mcl < -1) { client.error("%d is invalid for MaxCodeLines(mcl)", mcl); } else { client.setDebuggerClientMaxCodeLines(mcl); client.print("MaxCodeLines(mcl) is set to %d", mcl); } return; } listVars(client); }
void CmdMachine::onClientImpl(DebuggerClient &client) { if (DebuggerCommand::displayedHelp(client)) return; if (client.argCount() == 0) { help(client); return; } bool rpc = client.arg(1, "rpc"); if (rpc || client.arg(1, "connect")) { if (client.argCount() != 2) { help(client); return; } string host = client.argValue(2); int port = 0; size_t pos = host.find(":"); if (pos != string::npos) { if (!DebuggerClient::IsValidNumber(host.substr(pos + 1))) { client.error("Port needs to be a number"); help(client); return; } port = atoi(host.substr(pos + 1).c_str()); host = host.substr(0, pos); } if (rpc) { if (client.connectRPC(host, port)) { throw DebuggerConsoleExitException(); } } else { if (client.connect(host, port)) { throw DebuggerConsoleExitException(); } } if (!client.initializeMachine()) { throw DebuggerConsoleExitException(); } return; } if (client.arg(1, "disconnect")) { if (client.disconnect()) { throw DebuggerConsoleExitException(); } if (!client.initializeMachine()) { throw DebuggerConsoleExitException(); } return; } if (client.arg(1, "list")) { processList(client); return; } if (client.arg(1, "attach")) { DSandboxInfoPtr sandbox; string snum = client.argValue(2); if (DebuggerClient::IsValidNumber(snum)) { int num = atoi(snum.c_str()); sandbox = client.getSandbox(num); if (!sandbox) { processList(client, false); sandbox = client.getSandbox(num); if (!sandbox) { client.error("\"%s\" is not a valid sandbox index. Choose one from " "this list:", snum.c_str()); processList(client); return; } } } else { int argBase = 2; if (client.argCount() >= 2 && client.arg(2, "force")) { m_force = true; argBase++; } sandbox = DSandboxInfoPtr(new DSandboxInfo()); if (client.argCount() < argBase) { sandbox->m_user = client.getCurrentUser(); sandbox->m_name = "default"; } else if (client.argCount() == argBase) { sandbox->m_user = client.getCurrentUser(); sandbox->m_name = client.argValue(argBase); } else if (client.argCount() == argBase + 1) { sandbox->m_user = client.argValue(argBase); sandbox->m_name = client.argValue(argBase + 1); } else { help(client); return; } } if (AttachSandbox(client, sandbox, m_force)) { // Attach succeed, wait for next interrupt throw DebuggerConsoleExitException(); } return; } help(client); }
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." ); } } }
// Checks the command arguments, report errors and returning as appropriate. // Then communicates with the server to retrieve source information. Also // retrieves and updates location information stored in the client. void CmdList::onClient(DebuggerClient &client) { if (DebuggerCommand::displayedHelp(client)) return; if (client.argCount() > 1) { help(client); return; } int line = 0; m_line1 = m_line2 = 0; if (client.argCount() == 1) { std::string arg = client.argValue(1); if (DebuggerClient::IsValidNumber(arg)) { line = atoi(arg.c_str()); if (line <= 0) { client.error("A line number has to be a positive integer."); help(client); return; } m_line1 = line - DebuggerClient::CodeBlockSize/2; m_line2 = m_line1 + DebuggerClient::CodeBlockSize; } else if (arg.find("::") != std::string::npos) { if (!listFunctionOrClass(client)) { client.error("Unable to read specified method."); } return; } else { size_t pos = arg.find(':'); if (pos != std::string::npos) { m_file = arg.substr(0, pos); if (m_file.empty()) { client.error("File name cannot be empty."); help(client); return; } arg = arg.substr(pos + 1); } pos = arg.find('-'); if (pos != std::string::npos) { std::string line1 = arg.substr(0, pos); std::string line2 = arg.substr(pos + 1); if (!DebuggerClient::IsValidNumber(line1) || !DebuggerClient::IsValidNumber(line2)) { if (m_file.empty()) { m_file = arg; m_line1 = 1; m_line2 = DebuggerClient::CodeBlockSize; } else { client.error("Line numbers have to be integers."); help(client); return; } } else { m_line1 = atoi(line1.c_str()); m_line2 = atoi(line2.c_str()); if (line1.empty()) { m_line1 = m_line2 - DebuggerClient::CodeBlockSize; } if (line2.empty()) { m_line2 = m_line1 + DebuggerClient::CodeBlockSize; } if (m_line1 <= 0 || m_line2 <= 0) { client.error("Line numbers have to be positive integers."); help(client); return; } } } else { if (!DebuggerClient::IsValidNumber(arg)) { if (m_file.empty()) { if (client.argCount() == 1 && listFunctionOrClass(client)) { return; } m_file = arg; m_line1 = 1; m_line2 = DebuggerClient::CodeBlockSize; } else { client.error("A line number has to be an integer."); help(client); return; } } else { int line = atoi(arg.c_str()); if (line <= 0) { client.error("A line number has to be a positive integer."); help(client); return; } m_line1 = line - DebuggerClient::CodeBlockSize/2; m_line2 = m_line1 + DebuggerClient::CodeBlockSize; } } } } int charFocus0 = 0; int lineFocus1 = 0; int charFocus1 = 0; if (m_file.empty()) { getListLocation(client, line, charFocus0, lineFocus1, charFocus1); if (m_file.empty()) { listEvalCode(client); return; } } else if (m_file[0] == '/') { struct stat sb; stat(m_file.c_str(), &sb); if ((sb.st_mode & S_IFMT) == S_IFDIR) { client.setSourceRoot(m_file); client.info("PHP source root directory is set to %s", m_file.c_str()); return; } } if (!listFileRange(client, line, charFocus0, lineFocus1, charFocus1)) { client.error( "Unable to read specified function, class or source file location."); } }