| //===-- 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; |
| } |