summaryrefslogtreecommitdiff
path: root/tools/llvm-db/CLIDebugger.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/llvm-db/CLIDebugger.cpp')
-rw-r--r--tools/llvm-db/CLIDebugger.cpp308
1 files changed, 308 insertions, 0 deletions
diff --git a/tools/llvm-db/CLIDebugger.cpp b/tools/llvm-db/CLIDebugger.cpp
new file mode 100644
index 000000000000..1d2a8387a9c7
--- /dev/null
+++ b/tools/llvm-db/CLIDebugger.cpp
@@ -0,0 +1,308 @@
+//===-- CLIDebugger.cpp - Command Line Interface to the Debugger ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the main implementation of the Command Line Interface to
+// the debugger.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CLIDebugger.h"
+#include "CLICommand.h"
+#include "llvm/Debugger/SourceFile.h"
+#include "llvm/ADT/StringExtras.h"
+#include <iostream>
+using namespace llvm;
+
+/// CLIDebugger constructor - This initializes the debugger to its default
+/// state, and initializes the command table.
+///
+CLIDebugger::CLIDebugger()
+ : TheProgramInfo(0), TheRuntimeInfo(0), Prompt("(llvm-db) "), ListSize(10) {
+ // Initialize instance variables
+ CurrentFile = 0;
+ LineListedStart = 1;
+ LineListedEnd = 1;
+ LastCurrentFrame = 0;
+ CurrentLanguage = 0;
+
+ CLICommand *C;
+ //===--------------------------------------------------------------------===//
+ // Program startup and shutdown options
+ //
+ addCommand("file", new BuiltinCLICommand(
+ "Use specified file as the program to be debugged",
+ "The debugger looks in the current directory and the program $PATH for the"
+ " specified LLVM program. It then unloads the currently loaded program and"
+ " loads the specified program.\n",
+ &CLIDebugger::fileCommand));
+
+ addCommand("create", new BuiltinCLICommand(
+ "Start the program, halting its execution in main",
+ "This command creates an instance of the current program, but stops"
+ "\nexecution immediately.\n",
+ &CLIDebugger::createCommand));
+
+ addCommand("kill", new BuiltinCLICommand(
+ "Kills the execution of the current program being debugged", "",
+ &CLIDebugger::killCommand));
+
+ addCommand("quit", new BuiltinCLICommand(
+ "Exit the debugger", "",
+ &CLIDebugger::quitCommand));
+
+ //===--------------------------------------------------------------------===//
+ // Program execution commands
+ //
+ addCommand("run", C = new BuiltinCLICommand(
+ "Start the program running from the beginning", "",
+ &CLIDebugger::runCommand));
+ addCommand("r", C);
+
+ addCommand("cont", C = new BuiltinCLICommand(
+ "Continue program being debugged until the next stop point", "",
+ &CLIDebugger::contCommand));
+ addCommand("c", C); addCommand("fg", C);
+
+ addCommand("step", C = new BuiltinCLICommand(
+ "Step program until it reaches a new source line", "",
+ &CLIDebugger::stepCommand));
+ addCommand("s", C);
+
+ addCommand("next", C = new BuiltinCLICommand(
+ "Step program until it reaches a new source line, stepping over calls", "",
+ &CLIDebugger::nextCommand));
+ addCommand("n", C);
+
+ addCommand("finish", new BuiltinCLICommand(
+ "Execute until the selected stack frame returns",
+ "Upon return, the value returned is printed and put in the value history.\n",
+ &CLIDebugger::finishCommand));
+
+ //===--------------------------------------------------------------------===//
+ // Stack frame commands
+ //
+ addCommand("backtrace", C = new BuiltinCLICommand(
+ "Print backtrace of all stack frames, or innermost COUNT frames",
+ "FIXME: describe. Takes 'n', '-n' or 'full'\n",
+ &CLIDebugger::backtraceCommand));
+ addCommand("bt", C);
+
+ addCommand("up", new BuiltinCLICommand(
+ "Select and print stack frame that called this one",
+ "An argument says how many frames up to go.\n",
+ &CLIDebugger::upCommand));
+
+ addCommand("down", new BuiltinCLICommand(
+ "Select and print stack frame called by this one",
+ "An argument says how many frames down go.\n",
+ &CLIDebugger::downCommand));
+
+ addCommand("frame", C = new BuiltinCLICommand(
+ "Select and print a stack frame",
+ "With no argument, print the selected stack frame. (See also 'info frame').\n"
+ "An argument specifies the frame to select.\n",
+ &CLIDebugger::frameCommand));
+ addCommand("f", C);
+
+ //===--------------------------------------------------------------------===//
+ // Breakpoint related commands
+ //
+ addCommand("break", C = new BuiltinCLICommand(
+ "Set breakpoint at specified line or function",
+ "FIXME: describe.\n",
+ &CLIDebugger::breakCommand));
+ addCommand("b", C);
+
+
+ //===--------------------------------------------------------------------===//
+ // Miscellaneous commands
+ //
+ addCommand("info", new BuiltinCLICommand(
+ "Generic command for showing things about the program being debugged",
+ "info functions: display information about functions in the program.\ninfo"
+ " source : display information about the current source file.\ninfo source"
+ "s : Display source file names for the program\ninfo target : print status"
+ " of inferior process\n",
+ &CLIDebugger::infoCommand));
+
+ addCommand("list", C = new BuiltinCLICommand(
+ "List specified function or line",
+ "FIXME: document\n",
+ &CLIDebugger::listCommand));
+ addCommand("l", C);
+
+ addCommand("set", new BuiltinCLICommand(
+ "Change program or debugger variable",
+ "FIXME: document\n",
+ &CLIDebugger::setCommand));
+
+ addCommand("show", new BuiltinCLICommand(
+ "Generic command for showing things about the debugger",
+ "FIXME: document\n",
+ &CLIDebugger::showCommand));
+
+ addCommand("help", C = new BuiltinCLICommand(
+ "Prints information about available commands", "",
+ &CLIDebugger::helpCommand));
+ addCommand("h", C);
+}
+
+
+/// addCommand - Add a command to the CommandTable, potentially displacing a
+/// preexisting command.
+void CLIDebugger::addCommand(const std::string &Option, CLICommand *Cmd) {
+ assert(Cmd && "Cannot set a null command!");
+ CLICommand *&CS = CommandTable[Option];
+ if (CS == Cmd) return; // noop
+
+ // If we already have a command, decrement the command's reference count.
+ if (CS) {
+ CS->removeOptionName(Option);
+ CS->dropRef();
+ }
+ CS = Cmd;
+
+ // Remember that we are using this command.
+ Cmd->addRef();
+ Cmd->addOptionName(Option);
+}
+
+static bool isValidPrefix(const std::string &Prefix, const std::string &Option){
+ return Prefix.size() <= Option.size() &&
+ Prefix == std::string(Option.begin(), Option.begin()+Prefix.size());
+}
+
+/// getCommand - This looks up the specified command using a fuzzy match.
+/// If the string exactly matches a command or is an unambiguous prefix of a
+/// command, it returns the command. Otherwise it throws an exception
+/// indicating the possible ambiguous choices.
+CLICommand *CLIDebugger::getCommand(const std::string &Command) {
+
+ // Look up the command in the table.
+ std::map<std::string, CLICommand*>::iterator CI =
+ CommandTable.lower_bound(Command);
+
+ if (Command == "") {
+ throw "Null command should not get here!";
+ } else if (CI == CommandTable.end() ||
+ !isValidPrefix(Command, CI->first)) {
+ // If this command has no relation to anything in the command table,
+ // print the error message.
+ throw "Unknown command: '" + Command +
+ "'. Use 'help' for list of commands.";
+ } else if (CI->first == Command) {
+ // We have an exact match on the command
+ return CI->second;
+ } else {
+ // Otherwise, we have a prefix match. Check to see if this is
+ // unambiguous, and if so, run it.
+ std::map<std::string, CLICommand*>::iterator CI2 = CI;
+
+ // If the next command is a valid completion of this one, we are
+ // ambiguous.
+ if (++CI2 != CommandTable.end() && isValidPrefix(Command, CI2->first)) {
+ std::string ErrorMsg =
+ "Ambiguous command '" + Command + "'. Options: " + CI->first;
+ for (++CI; CI != CommandTable.end() &&
+ isValidPrefix(Command, CI->first); ++CI)
+ ErrorMsg += ", " + CI->first;
+ throw ErrorMsg;
+ } else {
+ // It's an unambiguous prefix of a command, use it.
+ return CI->second;
+ }
+ }
+}
+
+
+/// run - Start the debugger, returning when the user exits the debugger. This
+/// starts the main event loop of the CLI debugger.
+///
+int CLIDebugger::run() {
+ std::string Command;
+ std::cout << Prompt;
+
+ // Keep track of the last command issued, so that we can reissue it if the
+ // user hits enter as the command.
+ CLICommand *LastCommand = 0;
+ std::string LastArgs;
+
+ // Continue reading commands until the end of file.
+ while (getline(std::cin, Command)) {
+ std::string Arguments = Command;
+
+ // Split off the command from the arguments to the command.
+ Command = getToken(Arguments, " \t\n\v\f\r\\/;.*&");
+
+ try {
+ CLICommand *CurCommand;
+
+ if (Command == "") {
+ CurCommand = LastCommand;
+ Arguments = LastArgs;
+ } else {
+ CurCommand = getCommand(Command);
+ }
+
+ // Save the command we are running in case the user wants us to repeat it
+ // next time.
+ LastCommand = CurCommand;
+ LastArgs = Arguments;
+
+ // Finally, execute the command.
+ if (CurCommand)
+ CurCommand->runCommand(*this, Arguments);
+
+ } catch (int RetVal) {
+ // The quit command exits the command loop by throwing an integer return
+ // code.
+ return RetVal;
+ } catch (const std::string &Error) {
+ std::cout << "Error: " << Error << "\n";
+ } catch (const char *Error) {
+ std::cout << "Error: " << Error << "\n";
+ } catch (const NonErrorException &E) {
+ std::cout << E.getMessage() << "\n";
+ } catch (...) {
+ std::cout << "ERROR: Debugger caught unexpected exception!\n";
+ // Attempt to continue.
+ }
+
+ // Write the prompt to get the next bit of user input
+ std::cout << Prompt;
+ }
+
+ return 0;
+}
+
+
+/// askYesNo - Ask the user a question, and demand a yes/no response. If
+/// the user says yes, return true.
+///
+bool CLIDebugger::askYesNo(const std::string &Message) const {
+ std::string Answer;
+ std::cout << Message << " (y or n) " << std::flush;
+ while (getline(std::cin, Answer)) {
+ std::string Val = getToken(Answer);
+ if (getToken(Answer).empty()) {
+ if (Val == "yes" || Val == "y" || Val == "YES" || Val == "Y" ||
+ Val == "Yes")
+ return true;
+ if (Val == "no" || Val == "n" || Val == "NO" || Val == "N" ||
+ Val == "No")
+ return false;
+ }
+
+ std::cout << "Please answer y or n.\n" << Message << " (y or n) "
+ << std::flush;
+ }
+
+ // Ran out of input?
+ return false;
+}