diff options
author | Chris Lattner <sabre@nondot.org> | 2004-01-05 05:27:31 +0000 |
---|---|---|
committer | Chris Lattner <sabre@nondot.org> | 2004-01-05 05:27:31 +0000 |
commit | 7af5c12597b4a99a501e1e7ddbecb72b69400488 (patch) | |
tree | 6d0b9561b6d55c11cc652afc5862a0b7c5b6e35a /tools | |
parent | 6dddfff7502774efa50a6650aa6b5a0b963c83e4 (diff) |
Initial checkin of the LLVM Source-Level Debugger. This is incomplete, but
a good start. The status is documented in docs/SourceLevelDebugging.html
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@10687 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools')
-rw-r--r-- | tools/llvm-db/CLICommand.h | 110 | ||||
-rw-r--r-- | tools/llvm-db/CLIDebugger.cpp | 285 | ||||
-rw-r--r-- | tools/llvm-db/CLIDebugger.h | 200 | ||||
-rw-r--r-- | tools/llvm-db/Commands.cpp | 803 | ||||
-rw-r--r-- | tools/llvm-db/Makefile | 60 | ||||
-rw-r--r-- | tools/llvm-db/llvm-db.cpp | 88 |
6 files changed, 1546 insertions, 0 deletions
diff --git a/tools/llvm-db/CLICommand.h b/tools/llvm-db/CLICommand.h new file mode 100644 index 0000000000..25451e747f --- /dev/null +++ b/tools/llvm-db/CLICommand.h @@ -0,0 +1,110 @@ +//===- CLICommand.h - Classes used to represent commands --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the LLVM research group and is distributed under +// the University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a small class hierarchy used to represent the various types +// of commands in the CLI debugger front-end. +// +//===----------------------------------------------------------------------===// + +#ifndef CLICOMMAND_H +#define CLICOMMAND_H + +#include <string> +#include <vector> + +namespace llvm { + class CLIDebugger; + + /// CLICommand - Base class of the hierarchy, used to provide the abstract + /// interface common to all commands. + /// + class CLICommand { + /// ShortHelp, LongHelp - The short and long helps strings printed for the + /// command. The ShortHelp string should be a single line of text without a + /// newline. The LongHelp string should be a full description with + /// terminating newline. + std::string ShortHelp, LongHelp; + + /// RefCount - This contains the number of entries in the CLIDebugger + /// CommandTable that points to this command. + unsigned RefCount; + + /// OptionNames - This contains a list of names for the option. Keeping + /// track of this is done just to make the help output more helpful. + /// + std::vector<std::string> OptionNames; + public: + CLICommand(const std::string &SH, const std::string &LH) + : ShortHelp(SH), LongHelp(LH), RefCount(0) {} + + virtual ~CLICommand() {} + + /// addRef/dropRef - Implement a simple reference counting scheme to make + /// sure we delete commands that are no longer used. + void addRef() { ++RefCount; } + void dropRef() { + if (--RefCount == 0) delete this; + } + + /// getPrimaryOptionName - Return the first name the option was added under. + /// This is the name we report for the option in the help output. + std::string getPrimaryOptionName() const { + return OptionNames.empty() ? "" : OptionNames[0]; + } + + /// getOptionName - Return all of the names the option is registered as. + /// + const std::vector<std::string> &getOptionNames() const { + return OptionNames; + } + + /// addOptionName - Add a name that this option is known as. + /// + void addOptionName(const std::string &Name) { + OptionNames.push_back(Name); + } + + /// removeOptionName - Eliminate one of the names for this option. + /// + void removeOptionName(const std::string &Name) { + unsigned i = 0; + for (; OptionNames[i] != Name; ++i) + assert(i+1 < OptionNames.size() && "Didn't find option name!"); + OptionNames.erase(OptionNames.begin()+i); + } + + + /// getShortHelp - Return the short help string for this command. + /// + const std::string &getShortHelp() { return ShortHelp; } + + /// getLongHelp - Return the long help string for this command, if it + /// exists. + const std::string &getLongHelp() { return LongHelp; } + + virtual void runCommand(CLIDebugger &D, std::string &Arguments) = 0; + }; + + /// BuiltinCLICommand - This class represents commands that are built directly + /// into the debugger. + class BuiltinCLICommand : public CLICommand { + // Impl - Pointer to the method that implements the command + void (CLIDebugger::*Impl)(std::string&); + public: + BuiltinCLICommand(const std::string &ShortHelp, const std::string &LongHelp, + void (CLIDebugger::*impl)(std::string&)) + : CLICommand(ShortHelp, LongHelp), Impl(impl) {} + + void runCommand(CLIDebugger &D, std::string &Arguments) { + (D.*Impl)(Arguments); + } + }; +} + +#endif diff --git a/tools/llvm-db/CLIDebugger.cpp b/tools/llvm-db/CLIDebugger.cpp new file mode 100644 index 0000000000..2b3937a075 --- /dev/null +++ b/tools/llvm-db/CLIDebugger.cpp @@ -0,0 +1,285 @@ +//===-- CLIDebugger.cpp - Command Line Interface to the Debugger ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the LLVM research group and 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 "Support/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 current program, stopping execution in main", + "This command creates an instance of the current program, but stops" + " execution immediately.", + &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", + "FIXME: document\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 not implemented yet."; + } 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; + + // 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 { + // Look up the command and execute it. + getCommand(Command)->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; +} diff --git a/tools/llvm-db/CLIDebugger.h b/tools/llvm-db/CLIDebugger.h new file mode 100644 index 0000000000..046c2e8429 --- /dev/null +++ b/tools/llvm-db/CLIDebugger.h @@ -0,0 +1,200 @@ +//===- CLIDebugger.h - LLVM Command Line Interface Debugger -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the LLVM research group and is distributed under +// the University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the CLIDebugger class, which implements a command line +// interface to the LLVM Debugger library. +// +//===----------------------------------------------------------------------===// + +#ifndef CLIDEBUGGER_H +#define CLIDEBUGGER_H + +#include "llvm/Debugger/Debugger.h" +#include <map> + +namespace llvm { + class CLICommand; + class SourceFile; + class SourceLanguage; + class ProgramInfo; + class RuntimeInfo; + + /// CLIDebugger - This class implements the command line interface for the + /// LLVM debugger. + class CLIDebugger { + /// Dbg - The low-level LLVM debugger object that we use to do our dirty + /// work. + Debugger Dbg; + + /// CommandTable - This table contains a mapping from command names to the + /// CLICommand object that implements the command. + std::map<std::string, CLICommand*> CommandTable; + + //===------------------------------------------------------------------===// + // Data related to the program that is currently loaded. Note that the Dbg + // variable also captures some information about the loaded program. This + // pointer is non-null iff Dbg.isProgramLoaded() is true. + // + ProgramInfo *TheProgramInfo; + + //===------------------------------------------------------------------===// + // Data related to the program that is currently executing, but has stopped. + // Note that the Dbg variable also captures some information about the + // loaded program. This pointer is non-null iff Dbg.isProgramRunning() is + // true. + // + RuntimeInfo *TheRuntimeInfo; + + /// LastCurrentFrame - This variable holds the Frame ID of the top-level + /// stack frame from the last time that the program was executed. We keep + /// this because we only want to print the source location when the current + /// function changes. + void *LastCurrentFrame; + + //===------------------------------------------------------------------===// + // Data directly exposed through the debugger prompt + // + std::string Prompt; // set prompt, show prompt + unsigned ListSize; // set listsize, show listsize + + //===------------------------------------------------------------------===// + // Data to support user interaction + // + + /// CurrentFile - The current source file we are inspecting, or null if + /// none. + const SourceFile *CurrentFile; + unsigned LineListedStart, LineListedEnd; + + /// CurrentLanguage - This contains the source language in use, if one is + /// explicitly set by the user. If this is null (the default), the language + /// is automatically determined from the current stack frame. + /// + const SourceLanguage *CurrentLanguage; + + public: + CLIDebugger(); + + /// getDebugger - Return the current LLVM debugger implementation being + /// used. + Debugger &getDebugger() { return Dbg; } + + /// run - Start the debugger, returning when the user exits the debugger. + /// This starts the main event loop of the CLI debugger. + /// + int run(); + + /// addCommand - Add a command to the CommandTable, potentially displacing a + /// preexisting command. + void addCommand(const std::string &Option, CLICommand *Cmd); + + /// addSourceDirectory - Add a directory to search when looking for the + /// source code of the program. + void addSourceDirectory(const std::string &Dir) { + // FIXME: implement + } + + /// getCurrentLanguage - Return the current source language that the user is + /// playing around with. This is aquired from the current stack frame of a + /// running program if one exists, but this value can be explicitly set by + /// the user as well. + const SourceLanguage &getCurrentLanguage() const; + + /// getProgramInfo - Return a reference to the ProgramInfo object for the + /// currently loaded program. If there is no program loaded, throw an + /// exception. + ProgramInfo &getProgramInfo() const { + if (TheProgramInfo == 0) + throw "No program is loaded."; + return *TheProgramInfo; + } + + /// getRuntimeInfo - Return a reference to the current RuntimeInfo object. + /// If there is no program running, throw an exception. + RuntimeInfo &getRuntimeInfo() const { + if (TheRuntimeInfo == 0) + throw "No program is running."; + return *TheRuntimeInfo; + } + + private: // Internal implementation methods + + /// 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 *getCommand(const std::string &Command); + + /// askYesNo - Ask the user a question, and demand a yes/no response. If + /// the user says yes, return true. + bool askYesNo(const std::string &Message) const; + + /// printProgramLocation - Given a loaded and created child process that has + /// stopped, print its current source location. + void printProgramLocation(bool PrintLocation = true); + + /// eliminateRunInfo - We are about to run the program. Forget any state + /// about how the program used to be stopped. + void eliminateRunInfo(); + + /// programStoppedSuccessfully - This method updates internal data + /// structures to reflect the fact that the program just executed a while, + /// and has successfully stopped. + void programStoppedSuccessfully(); + + public: /// Builtin debugger commands, invokable by the user + // Program startup and shutdown options + void fileCommand(std::string &Options); // file + void createCommand(std::string &Options); // create + void killCommand(std::string &Options); // kill + void quitCommand(std::string &Options); // quit + + // Program execution commands + void runCommand(std::string &Options); // run|r + void contCommand(std::string &Options); // cont|c|fg + void stepCommand(std::string &Options); // step|s [count] + void nextCommand(std::string &Options); // next|n [count] + void finishCommand(std::string &Options); // finish + + // Stack frame commands + void backtraceCommand(std::string &Options); // backtrace|bt [count] + void upCommand(std::string &Options); // up + void downCommand(std::string &Options); // down + void frameCommand(std::string &Options); // frame + + + // Breakpoint related commands + void breakCommand(std::string &Options); // break|b <id> + + // Miscellaneous commands + void infoCommand(std::string &Options); // info + void listCommand(std::string &Options); // list + void setCommand(std::string &Options); // set + void showCommand(std::string &Options); // show + void helpCommand(std::string &Options); // help + + private: + /// startProgramRunning - If the program has been updated, reload it, then + /// start executing the program. + void startProgramRunning(); + + /// parseLineSpec - Parses a line specifier, for use by the 'list' command. + /// If SourceFile is returned as a void pointer, then it was not specified. + /// If the line specifier is invalid, an exception is thrown. + void parseLineSpec(std::string &LineSpec, const SourceFile *&SourceFile, + unsigned &LineNo); + + /// printSourceLine - Print the specified line of the current source file. + /// If the specified line is invalid (the source file could not be loaded or + /// the line number is out of range), don't print anything, but return true. + bool printSourceLine(unsigned LineNo); + }; +} + +#endif diff --git a/tools/llvm-db/Commands.cpp b/tools/llvm-db/Commands.cpp new file mode 100644 index 0000000000..9d0bcbada5 --- /dev/null +++ b/tools/llvm-db/Commands.cpp @@ -0,0 +1,803 @@ +//===-- Commands.cpp - Implement various commands for the CLI -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by the LLVM research group and is distributed under +// the University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements many builtin user commands. +// +//===----------------------------------------------------------------------===// + +#include "CLIDebugger.h" +#include "CLICommand.h" +#include "llvm/Debugger/ProgramInfo.h" +#include "llvm/Debugger/RuntimeInfo.h" +#include "llvm/Debugger/SourceLanguage.h" +#include "llvm/Debugger/SourceFile.h" +#include "llvm/Debugger/InferiorProcess.h" +#include "Support/FileUtilities.h" +#include "Support/StringExtras.h" +#include <iostream> +using namespace llvm; + +/// getCurrentLanguage - Return the current source language that the user is +/// playing around with. This is aquired from the current stack frame of a +/// running program if one exists, but this value can be explicitly set by the +/// user as well. +const SourceLanguage &CLIDebugger::getCurrentLanguage() const { + // If the user explicitly switched languages with 'set language', use what + // they asked for. + if (CurrentLanguage) { + return *CurrentLanguage; + } else if (Dbg.isProgramRunning()) { + // Otherwise, if the program is running, infer the current language from it. + const GlobalVariable *FuncDesc = + getRuntimeInfo().getCurrentFrame().getFunctionDesc(); + return getProgramInfo().getFunction(FuncDesc).getSourceFile().getLanguage(); + } else { + // Otherwise, default to C like GDB apparently does. + return SourceLanguage::getCFamilyInstance(); + } +} + +/// startProgramRunning - If the program has been updated, reload it, then +/// start executing the program. +void CLIDebugger::startProgramRunning() { + eliminateRunInfo(); + + // If the program has been modified, reload it! + std::string Program = Dbg.getProgramPath(); + if (TheProgramInfo->getProgramTimeStamp() != getFileTimestamp(Program)) { + std::cout << "'" << Program << "' has changed; re-reading program.\n"; + + // Unload an existing program. This kills the program if necessary. + Dbg.unloadProgram(); + delete TheProgramInfo; + TheProgramInfo = 0; + CurrentFile = 0; + + Dbg.loadProgram(Program); + TheProgramInfo = new ProgramInfo(Dbg.getProgram()); + } + + std::cout << "Starting program: " << Dbg.getProgramPath() << "\n"; + Dbg.createProgram(); + + // There was no current frame. + LastCurrentFrame = 0; +} + +/// printSourceLine - Print the specified line of the current source file. +/// If the specified line is invalid (the source file could not be loaded or +/// the line number is out of range), don't print anything, but return true. +bool CLIDebugger::printSourceLine(unsigned LineNo) { + assert(CurrentFile && "There is no current source file to print!"); + const char *LineStart, *LineEnd; + CurrentFile->getSourceLine(LineNo-1, LineStart, LineEnd); + if (LineStart == 0) return true; + std::cout << LineNo; + + // If this is the line the program is currently stopped at, print a marker. + if (Dbg.isProgramRunning()) { + unsigned CurLineNo, CurColNo; + const SourceFileInfo *CurSFI; + getRuntimeInfo().getCurrentFrame().getSourceLocation(CurLineNo, CurColNo, + CurSFI); + + if (CurLineNo == LineNo && CurrentFile == &CurSFI->getSourceText()) + std::cout << " ->"; + } + + std::cout << "\t" << std::string(LineStart, LineEnd) << "\n"; + return false; +} + +/// printProgramLocation - Print a line of the place where the current stack +/// frame has stopped and the source line it is on. +/// +void CLIDebugger::printProgramLocation(bool PrintLocation) { + assert(Dbg.isProgramLoaded() && Dbg.isProgramRunning() && + "Error program is not loaded and running!"); + + // Figure out where the program stopped... + StackFrame &SF = getRuntimeInfo().getCurrentFrame(); + unsigned LineNo, ColNo; + const SourceFileInfo *FileDesc; + SF.getSourceLocation(LineNo, ColNo, FileDesc); + + // If requested, print out some program information about WHERE we are. + if (PrintLocation) { + // FIXME: print the current function arguments + if (const GlobalVariable *FuncDesc = SF.getFunctionDesc()) + std::cout << getProgramInfo().getFunction(FuncDesc).getSymbolicName(); + else + std::cout << "<unknown function>"; + + CurrentFile = &FileDesc->getSourceText(); + + std::cout << " at " << CurrentFile->getFilename() << ":" << LineNo; + if (ColNo) std::cout << ":" << ColNo << "\n"; + } + + if (printSourceLine(LineNo)) + std::cout << "<could not load source file>\n"; + else { + LineListedStart = LineNo-ListSize/2+1; + if ((int)LineListedStart < 1) LineListedStart = 1; + LineListedEnd = LineListedStart+1; + } +} + +/// eliminateRunInfo - We are about to run the program. Forget any state +/// about how the program used to be stopped. +void CLIDebugger::eliminateRunInfo() { + delete TheRuntimeInfo; + TheRuntimeInfo = 0; +} + +/// programStoppedSuccessfully - This method updates internal data +/// structures to reflect the fact that the program just executed a while, +/// and has successfully stopped. +void CLIDebugger::programStoppedSuccessfully() { + assert(TheRuntimeInfo==0 && "Someone forgot to release the old RuntimeInfo!"); + + TheRuntimeInfo = new RuntimeInfo(TheProgramInfo, Dbg.getRunningProcess()); + + // FIXME: if there are any breakpoints at the current location, print them as + // well. + + // Since the program as successfully stopped, print its location. + void *CurrentFrame = getRuntimeInfo().getCurrentFrame().getFrameID(); + printProgramLocation(CurrentFrame != LastCurrentFrame); + LastCurrentFrame = CurrentFrame; +} + + + +/// getUnsignedIntegerOption - Get an unsigned integer number from the Val +/// string. Check to make sure that the string contains an unsigned integer +/// token, and if not, throw an exception. If isOnlyOption is set, also throw +/// an exception if there is extra junk at the end of the string. +static unsigned getUnsignedIntegerOption(const char *Msg, std::string &Val, + bool isOnlyOption = true) { + std::string Tok = getToken(Val); + if (Tok.empty() || (isOnlyOption && !getToken(Val).empty())) + throw std::string(Msg) + " expects an unsigned integer argument."; + + char *EndPtr; + unsigned Result = strtoul(Tok.c_str(), &EndPtr, 0); + if (EndPtr != Tok.c_str()+Tok.size()) + throw std::string(Msg) + " expects an unsigned integer argument."; + + return Result; +} + +/// getOptionalUnsignedIntegerOption - This method is just like +/// getUnsignedIntegerOption, but if the argument value is not specified, a +/// default is returned instead of causing an error. +static unsigned +getOptionalUnsignedIntegerOption(const char *Msg, unsigned Default, + std::string &Val, bool isOnlyOption = true) { + // Check to see if the value was specified... + std::string TokVal = getToken(Val); + if (TokVal.empty()) return Default; + + // If it was specified, add it back to the value we are parsing... + Val = TokVal+Val; + + // And parse normally. + return getUnsignedIntegerOption(Msg, Val, isOnlyOption); +} + + +//===----------------------------------------------------------------------===// +// Program startup and shutdown options +//===----------------------------------------------------------------------===// + + +/// file command - If the user specifies an option, search the PATH for the +/// specified program/bytecode file and load it. If the user does not specify +/// an option, unload the current program. +void CLIDebugger::fileCommand(std::string &Options) { + std::string Prog = getToken(Options); + if (!getToken(Options).empty()) + throw "file command takes at most one argument."; + + // Check to make sure the user knows what they are doing + if (Dbg.isProgramRunning() && + !askYesNo("A program is already loaded. Kill it?")) + return; + + // Unload an existing program. This kills the program if necessary. + eliminateRunInfo(); + delete TheProgramInfo; + TheProgramInfo = 0; + Dbg.unloadProgram(); + CurrentFile = 0; + + // If requested, start the new program. + if (Prog.empty()) { + std::cout << "Unloaded program.\n"; + } else { + std::cout << "Loading program... " << std::flush; + Dbg.loadProgram(Prog); + assert(Dbg.isProgramLoaded() && + "loadProgram succeeded, but not program loaded!"); + TheProgramInfo = new ProgramInfo(Dbg.getProgram()); + std::cout << "success loading '" << Dbg.getProgramPath() << "'!\n"; + } +} + + +void CLIDebugger::createCommand(std::string &Options) { + if (!getToken(Options).empty()) + throw "create command does not take any arguments."; + if (!Dbg.isProgramLoaded()) throw "No program loaded."; + if (Dbg.isProgramRunning() && + !askYesNo("The program is already running. Restart from the beginning?")) + return; + + // Start the program running. + startProgramRunning(); + + // The program stopped! + programStoppedSuccessfully(); +} + +void CLIDebugger::killCommand(std::string &Options) { + if (!getToken(Options).empty()) + throw "kill command does not take any arguments."; + if (!Dbg.isProgramRunning()) + throw "No program is currently being run."; + + if (askYesNo("Kill the program being debugged?")) + Dbg.killProgram(); + eliminateRunInfo(); +} + +void CLIDebugger::quitCommand(std::string &Options) { + if (!getToken(Options).empty()) + throw "quit command does not take any arguments."; + + if (Dbg.isProgramRunning() && + !askYesNo("The program is running. Exit anyway?")) + return; + + // Throw exception to get out of the user-input loop. + throw 0; +} + + +//===----------------------------------------------------------------------===// +// Program execution commands +//===----------------------------------------------------------------------===// + +void CLIDebugger::runCommand(std::string &Options) { + if (!getToken(Options).empty()) throw "run arguments not supported yet."; + if (!Dbg.isProgramLoaded()) throw "No program loaded."; + if (Dbg.isProgramRunning() && + !askYesNo("The program is already running. Restart from the beginning?")) + return; + + eliminateRunInfo(); + + // Start the program running. + startProgramRunning(); + + // Start the program running... + Options = ""; + contCommand(Options); +} + +void CLIDebugger::contCommand(std::string &Options) { + if (!getToken(Options).empty()) throw "cont argument not supported yet."; + if (!Dbg.isProgramRunning()) throw "Program is not running."; + + eliminateRunInfo(); + + Dbg.contProgram(); + + // The program stopped! + programStoppedSuccessfully(); +} + +void CLIDebugger::stepCommand(std::string &Options) { + if (!Dbg.isProgramRunning()) throw "Program is not running."; + + // Figure out how many times to step. + unsigned Amount = + getOptionalUnsignedIntegerOption("'step' command", 1, Options); + + eliminateRunInfo(); + + // Step the specified number of times. + for (; Amount; --Amount) + Dbg.stepProgram(); + + // The program stopped! + programStoppedSuccessfully(); +} + +void CLIDebugger::nextCommand(std::string &Options) { + if (!Dbg.isProgramRunning()) throw "Program is not running."; + unsigned Amount = + getOptionalUnsignedIntegerOption("'next' command", 1, Options); + + eliminateRunInfo(); + + for (; Amount; --Amount) + Dbg.nextProgram(); + + // The program stopped! + programStoppedSuccessfully(); +} + +void CLIDebugger::finishCommand(std::string &Options) { + if (!getToken(Options).empty()) + throw "finish command does not take any arguments."; + if (!Dbg.isProgr |