void CmdException::onClientImpl(DebuggerClient &client) { if (DebuggerCommand::displayedHelp(client)) return; if (client.argCount() == 0) { help(client); return; } bool regex = false; BreakPointInfo::State state = BreakPointInfo::Always; int index = 1; if (client.arg(1, "regex")) { regex = true; index++; } else if (client.arg(1, "once")) { state = BreakPointInfo::Once; index++; } BreakPointInfoPtr bpi(new BreakPointInfo(regex, state, ExceptionThrown, client.argValue(index), "")); if (!addToBreakpointListAndUpdateServer(client, bpi, index)) { client.tutorial( "This is the order of different arguments:\n" "\n" "\t[e]xception [r]egex|[o]nce {exp} if|&& {php}\n" "\n" "These are the components in an exception {exp}:\n" "\n" "\terror@{url}" "\t{namespace}::{cls}@{url}" "\n" ); } }
// If the first argument of the command is "help" or "?" // this displays help text for the command and returns true. // Otherwise it returns false. bool DebuggerCommand::displayedHelp(DebuggerClient &client) { TRACE(2, "DebuggerCommand::displayedHelp\n"); if (client.arg(1, "help") || client.arg(1, "?")) { help(client); return true; } return false; }
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 CmdPrint::list(DebuggerClient &client) { if (client.arg(1, "clear")) { client.addCompletion("all"); return; } client.addCompletion(DebuggerClient::AutoCompleteCode); if (client.argCount() == 0) { client.addCompletion(Formats); client.addCompletion("always"); client.addCompletion("list"); client.addCompletion("clear"); } else if (client.argCount() == 1 && client.arg(1, "always")) { client.addCompletion(Formats); } }
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 CmdHelp::list(DebuggerClient &client) { if (client.argCount() == 0) { client.addCompletion(DebuggerClient::GetCommands()); client.addCompletion("tutorial"); client.addCompletion("start"); } else if (client.arg(1, "tutorial")) { client.addCompletion("on"); client.addCompletion("off"); client.addCompletion("auto"); } }
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; } } }
void CmdInternalTesting::onClientImpl(DebuggerClient &client) { TRACE(2, "CmdInternalTesting::onClientImpl\n"); if (DebuggerCommand::displayedHelp(client)) return; if (client.argCount() == 0) { help(client); return; } client.info("Executing internal test..."); m_arg = client.argValue(1); if (client.arg(1, "badcmdtypesend")) { // Give the cmd a bad type and send it over. This should cause the proxy to // disconnect from us. m_type = KindOfInternalTestingBad; client.sendToServer(this); // Spin here and wait for the client to be marked as stopped // before going back to the event loop. This will give the local // proxy time to recgonize the bad cmd, terminate, and wait for // the client to stop. This will ensure that we always exit on the // same path on both proxy and client threads, and remove any // spurious output form ths test case. while (!client.internalTestingIsClientStopped()) { sleep(1); } throw DebuggerConsoleExitException(); // Expect no response } else if (client.arg(1, "badcmdtypereceive")) { client.xend<CmdInternalTesting>(this); return; } else if (client.arg(1, "shortcmdsend")) { m_arg = "shortcmd"; // Force send to drop a field. client.sendToServer(this); // See note above about this wait. while (!client.internalTestingIsClientStopped()) { sleep(1); } throw DebuggerConsoleExitException(); // Expect no response } else if (client.arg(1, "shortcmdreceive")) { client.xend<CmdInternalTesting>(this); return; } else if (client.arg(1, "segfaultClient")) { int *px = nullptr; *px = 42; } else if (client.arg(1, "segfaultServer")) { client.xend<CmdInternalTesting>(this); return; } help(client); }
void CmdPrint::processClear(DebuggerClient &client) { DebuggerClient::WatchPtrVec &watches = client.getWatches(); if (watches.empty()) { client.error("There is no watch expression to clear."); client.tutorial( "Use '[p]rint [a]lways ...' to set new watch expressions. " "Use '[p]rint ?|[h]elp' to read how to set them. " ); return; } if (client.arg(2, "all")) { watches.clear(); client.info("All watch expressions are cleared."); return; } string snum = client.argValue(2); if (!DebuggerClient::IsValidNumber(snum)) { client.error("'[p]rint [c]lear' needs an {index} argument."); client.tutorial( "You will have to run '[p]rint [l]ist' first to see a list of valid " "numbers or indices to specify." ); return; } int num = atoi(snum.c_str()) - 1; if (num < 0 || num >= (int)watches.size()) { client.error("\"%s\" is not a valid index. Choose one from this list:", snum.c_str()); processList(client); return; } watches.erase(watches.begin() + num); }
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 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." ); } } }