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/llvm-db/Commands.cpp | |
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/llvm-db/Commands.cpp')
-rw-r--r-- | tools/llvm-db/Commands.cpp | 803 |
1 files changed, 803 insertions, 0 deletions
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.isProgramRunning()) throw "Program is not running."; + + // Figure out where we are exactly. If the user requests that we return from + // a frame that is not the top frame, make sure we get it. + void *CurrentFrame = getRuntimeInfo().getCurrentFrame().getFrameID(); + + eliminateRunInfo(); + + Dbg.finishProgram(CurrentFrame); + + // The program stopped! + programStoppedSuccessfully(); +} + +//===----------------------------------------------------------------------===// +// Stack frame commands +//===----------------------------------------------------------------------===// + +void CLIDebugger::backtraceCommand(std::string &Options) { + // Accepts "full", n, -n + if (!getToken(Options).empty()) + throw "FIXME: bt command argument not implemented yet!"; + + RuntimeInfo &RI = getRuntimeInfo(); + ProgramInfo &PI = getProgramInfo(); + + try { + for (unsigned i = 0; ; ++i) { + StackFrame &SF = RI.getStackFrame(i); + std::cout << "#" << i; + if (i == RI.getCurrentFrameIdx()) + std::cout << " ->"; + std::cout << "\t" << SF.getFrameID() << " in "; + if (const GlobalVariable *G = SF.getFunctionDesc()) + std::cout << PI.getFunction(G).getSymbolicName(); + + unsigned LineNo, ColNo; + const SourceFileInfo *SFI; + SF.getSourceLocation(LineNo, ColNo, SFI); + if (!SFI->getBaseName().empty()) { + std::cout << " at " << SFI->getBaseName(); + if (LineNo) { + std::cout << ":" << LineNo; + if (ColNo) + std::cout << ":" << ColNo; + } + } + + // FIXME: when we support shared libraries, we should print ' from foo.so' + // if the stack frame is from a different object than the current one. + + std::cout << "\n"; + } + } catch (...) { + // Stop automatically when we run off the bottom of the stack. + } +} + +void CLIDebugger::upCommand(std::string &Options) { + unsigned Num = + getOptionalUnsignedIntegerOption("'up' command", 1, Options); + + RuntimeInfo &RI = getRuntimeInfo(); + unsigned CurFrame = RI.getCurrentFrameIdx(); + + // Check to see if we go can up the specified number of frames. + try { + RI.getStackFrame(CurFrame+Num); + } catch (...) { + if (Num == 1) + throw "Initial frame selected; you cannot go up."; + else + throw "Cannot go up " + utostr(Num) + " frames!"; + } + + RI.setCurrentFrameIdx(CurFrame+Num); + printProgramLocation(); +} + +void CLIDebugger::downCommand(std::string &Options) { + unsigned Num = + getOptionalUnsignedIntegerOption("'down' command", 1, Options); + + RuntimeInfo &RI = getRuntimeInfo(); + unsigned CurFrame = RI.getCurrentFrameIdx(); + + // Check to see if we can go up the specified number of frames. + if (CurFrame < Num) + if (Num == 1) + throw "Bottom (i.e., innermost) frame selected; you cannot go down."; + else + throw "Cannot go down " + utostr(Num) + " frames!"; + + RI.setCurrentFrameIdx(CurFrame-Num); + printProgramLocation(); +} + +void CLIDebugger::frameCommand(std::string &Options) { + RuntimeInfo &RI = getRuntimeInfo(); + unsigned CurFrame = RI.getCurrentFrameIdx(); + + unsigned Num = + getOptionalUnsignedIntegerOption("'frame' command", CurFrame, Options); + + // Check to see if we go to the specified frame. + RI.getStackFrame(Num); + + RI.setCurrentFrameIdx(Num); + printProgramLocation(); +} + + +//===----------------------------------------------------------------------===// +// Breakpoint related commands +//===----------------------------------------------------------------------===// + +void CLIDebugger::breakCommand(std::string &Options) { + throw "breakpoints not implemented yet!"; +} + +//===----------------------------------------------------------------------===// +// Miscellaneous commands +//===----------------------------------------------------------------------===// + +void CLIDebugger::infoCommand(std::string &Options) { + std::string What = getToken(Options); + + if (What.empty() || !getToken(Options).empty()) + throw "info command expects exactly one argument."; + + if (What == "frame") { + } else if (What == "functions") { + const std::map<const GlobalVariable*, SourceFunctionInfo*> &Functions + = getProgramInfo().getSourceFunctions(); + std::cout << "All defined functions:\n"; + // FIXME: GDB groups these by source file. We could do that I guess. + for (std::map<const GlobalVariable*, SourceFunctionInfo*>::const_iterator + I = Functions.begin(), E = Functions.end(); I != E; ++I) { + std::cout << I->second->getSymbolicName() << "\n"; + } + + } else if (What == "source") { + if (CurrentFile == 0) + throw "No current source file."; + + // Get the SourceFile information for the current file. + const SourceFileInfo &SF = + getProgramInfo().getSourceFile(CurrentFile->getDescriptor()); + + std::cout << "Current source file is: " << SF.getBaseName() << "\n" + << "Compilation directory is: " << SF.getDirectory() << "\n"; + if (unsigned NL = CurrentFile->getNumLines()) + std::cout << "Located in: " << CurrentFile->getFilename() << "\n" + << "Contains " << NL << " lines\n"; + else + std::cout << "Could not find source file.\n"; + std::cout << "Source language is " + << SF.getLanguage().getSourceLanguageName() << "\n"; + + } else if (What == "sources") { + const std::map<const GlobalVariable*, SourceFileInfo*> &SourceFiles = + getProgramInfo().getSourceFiles(); + std::cout << "Source files for the program:\n"; + for (std::map<const GlobalVariable*, SourceFileInfo*>::const_iterator I = + SourceFiles.begin(), E = SourceFiles.end(); I != E;) { + std::cout << I->second->getDirectory() << "/" + << I->second->getBaseName(); + ++I; + if (I != E) std::cout << ", "; + } + std::cout << "\n"; + } else if (What == "target") { + std::cout << Dbg.getRunningProcess().getStatus(); + } else { + // See if this is something handled by the current language. + if (getCurrentLanguage().printInfo(What)) + return; + + throw "Unknown info command '" + What + "'. Try 'help info'."; + } +} + +/// 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 CLIDebugger::parseLineSpec(std::string &LineSpec, + const SourceFile *&SourceFile, + unsigned &LineNo) { + SourceFile = 0; + LineNo = 0; + + // First, check to see if we have a : separator. + std::string FirstPart = getToken(LineSpec, ":"); + std::string SecondPart = getToken(LineSpec, ":"); + if (!getToken(LineSpec).empty()) throw "Malformed line specification!"; + + // If there is no second part, we must have either "function", "number", + // "+offset", or "-offset". + if (SecondPart.empty()) { + if (FirstPart.empty()) throw "Malformed line specification!"; + if (FirstPart[0] == '+') { + FirstPart.erase(FirstPart.begin(), FirstPart.begin()+1); + // For +n, return LineListedEnd+n + LineNo = LineListedEnd + + getUnsignedIntegerOption("Line specifier '+'", FirstPart); + + } else if (FirstPart[0] == '-') { + FirstPart.erase(FirstPart.begin(), FirstPart.begin()+1); + // For -n, return LineListedEnd-n + LineNo = LineListedEnd - + getUnsignedIntegerOption("Line specifier '-'", FirstPart); + if ((int)LineNo < 1) LineNo = 1; + } else if (FirstPart[0] == '*') { + throw "Address expressions not supported as source locations!"; + } else { + // Ok, check to see if this is just a line number. + std::string Saved = FirstPart; + try { + LineNo = getUnsignedIntegerOption("", Saved); + } catch (...) { + // Ok, it's not a valid line number. It must be a source-language + // entity name. + std::string Name = getToken(FirstPart); + if (!getToken(FirstPart).empty()) + throw "Extra junk in line specifier after '" + Name + "'."; + SourceFunctionInfo *SFI = + getCurrentLanguage().lookupFunction(Name, getProgramInfo(), + TheRuntimeInfo); + if (SFI == 0) + throw "Unknown identifier '" + Name + "'."; + + unsigned L, C; + SFI->getSourceLocation(L, C); + if (L == 0) throw "Could not locate '" + Name + "'!"; + LineNo = L; + SourceFile = &SFI->getSourceFile().getSourceText(); + return; + } + } + + } else { + // Ok, this must be a filename qualified line number or function name. + // First, figure out the source filename. + std::string SourceFilename = getToken(FirstPart); + if (!getToken(FirstPart).empty()) + throw "Invalid filename qualified source location!"; + + // Next, check to see if this is just a line number. + std::string Saved = SecondPart; + try { + LineNo = getUnsignedIntegerOption("", Saved); + } catch (...) { + // Ok, it's not a valid line number. It must be a function name. + throw "FIXME: Filename qualified function names are not support " + "as line specifiers yet!"; + } + + // Ok, we got the line number. Now check out the source file name to make + // sure it's all good. If it is, return it. If not, throw exception. + SourceFile =&getProgramInfo().getSourceFile(SourceFilename).getSourceText(); + } +} + +void CLIDebugger::listCommand(std::string &Options) { + if (!Dbg.isProgramLoaded()) + throw "No program is loaded. Use the 'file' command."; + + // Handle "list foo," correctly, by returning " " as the second token + Options += " "; + + std::string FirstLineSpec = getToken(Options, ","); + std::string SecondLineSpec = getToken(Options, ","); + if (!getToken(Options, ",").empty()) + throw "list command only expects two source location specifiers!"; + + // StartLine, EndLine - The starting and ending line numbers to print. + unsigned StartLine = 0, EndLine = 0; + + if (SecondLineSpec.empty()) { // No second line specifier provided? + // Handle special forms like "", "+", "-", etc. + std::string TmpSpec = FirstLineSpec; + std::string Tok = getToken(TmpSpec); + if (getToken(TmpSpec).empty() && (Tok == "" || Tok == "+" || Tok == "-")) { + if (Tok == "+" || Tok == "") { + StartLine = LineListedEnd; + EndLine = StartLine + ListSize; + } else { + assert(Tok == "-"); + StartLine = LineListedStart-ListSize; + EndLine = LineListedStart; + if ((int)StartLine < 0) StartLine = 1; + } + } else { + // Must be a normal line specifier. + const SourceFile *File; + unsigned LineNo; + parseLineSpec(FirstLineSpec, File, LineNo); + + // If the user only specified one file specifier, we should display + // ListSize lines centered at the specified line. + if (File != 0) CurrentFile = File; + StartLine = LineNo - (ListSize+1)/2; + if ((int)StartLine < 0) StartLine = 1; + EndLine = StartLine + ListSize; + } + + } else { + // Parse two line specifiers... + const SourceFile *StartFile, *EndFile; + unsigned StartLineNo, EndLineNo; + parseLineSpec(FirstLineSpec, StartFile, StartLineNo); + unsigned SavedLLE = LineListedEnd; + LineListedEnd = StartLineNo; + try { + parseLineSpec(SecondLineSpec, EndFile, EndLineNo); + } catch (...) { + LineListedEnd = SavedLLE; + throw; + } + + // Inherit file specified by the first line spec if there was one. + if (EndFile == 0) EndFile = StartFile; + + if (StartFile != EndFile) + throw "Start and end line specifiers are in different files!"; + CurrentFile = StartFile; + StartLine = StartLineNo; + EndLine = EndLineNo+1; + } + + assert((int)StartLine > 0 && (int)EndLine > 0 && StartLine <= EndLine && + "Error reading line specifiers!"); + + // If there was no current file, and the user didn't specify one to list, we + // have an error. + if (CurrentFile == 0) + throw "There is no current file to list."; + + // Remember for next time. + LineListedStart = StartLine; + LineListedEnd = StartLine; + + for (unsigned LineNo = StartLine; LineNo != EndLine; ++LineNo) { + // Print the source line, unless it is invalid. + if (printSourceLine(LineNo)) + break; + LineListedEnd = LineNo+1; + } + + // If we didn't print any lines, find out why. + if (LineListedEnd == StartLine) { + // See if we can read line #0 from the file, if not, we couldn't load the + // file. + const char *LineStart, *LineEnd; + CurrentFile->getSourceLine(0, LineStart, LineEnd); + if (LineStart == 0) + throw "Could not load source file '" + CurrentFile->getFilename() + "'!"; + else + std::cout << "<end of file>\n"; + } +} + +void CLIDebugger::setCommand(std::string &Options) { + std::string What = getToken(Options); + + if (What.empty()) + throw "set command expects at least two arguments."; + if (What == "language") { + std::string Lang = getToken(Options); + if (!getToken(Options).empty()) + throw "set language expects one argument at most."; + if (Lang == "") { + std::cout << "The currently understood settings are:\n\n" + << "local or auto Automatic setting based on source file\n" + << "c Use the C language\n" + << "c++ Use the C++ language\n" + << "unknown Use when source language is not supported\n"; + } else if (Lang == "local" || Lang == "auto") { + CurrentLanguage = 0; + } else if (Lang == "c") { + CurrentLanguage = &SourceLanguage::getCFamilyInstance(); + } else if (Lang == "c++") { + CurrentLanguage = &SourceLanguage::getCPlusPlusInstance(); + } else if (Lang == "unknown") { + CurrentLanguage = &SourceLanguage::getUnknownLanguageInstance(); + } else { + throw "Unknown language '" + Lang + "'."; + } + + } else if (What == "listsize") { + ListSize = getUnsignedIntegerOption("'set prompt' command", Options); + } else if (What == "prompt") { + // Include any trailing whitespace or other tokens, but not leading + // whitespace. + Prompt = getToken(Options); // Strip leading whitespace + Prompt += Options; // Keep trailing whitespace or other stuff + } else { + // FIXME: Try to parse this as a source-language program expression. + throw "Don't know how to set '" + What + "'!"; + } +} + +void CLIDebugger::showCommand(std::string &Options) { + std::string What = getToken(Options); + + if (What.empty() || !getToken(Options).empty()) + throw "show command expects one argument."; + + if (What == "language") { + std::cout << "The current source language is '"; + if (CurrentLanguage) + std::cout << CurrentLanguage->getSourceLanguageName(); + else + std::cout << "auto; currently " + << getCurrentLanguage().getSourceLanguageName(); + std::cout << "'.\n"; + } else if (What == "listsize") { + std::cout << "Number of source lines llvm-db will list by default is " + << ListSize << ".\n"; + } else if (What == "prompt") { + std::cout << "llvm-db's prompt is \"" << Prompt << "\".\n"; + } else { + throw "Unknown show command '" + What + "'. Try 'help show'."; + } +} + +void CLIDebugger::helpCommand(std::string &Options) { + // Print out all of the commands in the CommandTable + std::string Command = getToken(Options); + if (!getToken(Options).empty()) + throw "help command takes at most one argument."; + + // Getting detailed help on a particular command? + if (!Command.empty()) { + CLICommand *C = getCommand(Command); + std::cout << C->getShortHelp() << ".\n" << C->getLongHelp(); + + // If there are aliases for this option, print them out. + const std::vector<std::string> &Names = C->getOptionNames(); + if (Names.size() > 1) { + std::cout << "The '" << Command << "' command is known as: '" + << Names[0] << "'"; + for (unsigned i = 1, e = Names.size(); i != e; ++i) + std::cout << ", '" << Names[i] << "'"; + std::cout << "\n"; + } + + } else { + unsigned MaxSize = 0; + for (std::map<std::string, CLICommand*>::iterator I = CommandTable.begin(), + E = CommandTable.end(); I != E; ++I) + if (I->first.size() > MaxSize && + I->first == I->second->getPrimaryOptionName()) + MaxSize = I->first.size(); + + // Loop over all of the commands, printing the short help version + for (std::map<std::string, CLICommand*>::iterator I = CommandTable.begin(), + E = CommandTable.end(); I != E; ++I) + if (I->first == I->second->getPrimaryOptionName()) + std::cout << I->first << std::string(MaxSize - I->first.size(), ' ') + << " - " << I->second->getShortHelp() << "\n"; + } +} |