From 7af5c12597b4a99a501e1e7ddbecb72b69400488 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 5 Jan 2004 05:27:31 +0000 Subject: [PATCH] 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 --- tools/llvm-db/CLICommand.h | 110 +++++ tools/llvm-db/CLIDebugger.cpp | 285 ++++++++++++ tools/llvm-db/CLIDebugger.h | 200 +++++++++ tools/llvm-db/Commands.cpp | 803 ++++++++++++++++++++++++++++++++++ tools/llvm-db/Makefile | 60 +++ tools/llvm-db/llvm-db.cpp | 88 ++++ 6 files changed, 1546 insertions(+) create mode 100644 tools/llvm-db/CLICommand.h create mode 100644 tools/llvm-db/CLIDebugger.cpp create mode 100644 tools/llvm-db/CLIDebugger.h create mode 100644 tools/llvm-db/Commands.cpp create mode 100644 tools/llvm-db/Makefile create mode 100644 tools/llvm-db/llvm-db.cpp diff --git a/tools/llvm-db/CLICommand.h b/tools/llvm-db/CLICommand.h new file mode 100644 index 00000000000..25451e747fc --- /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 +#include + +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 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 &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 00000000000..2b3937a075e --- /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 +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::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::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 00000000000..046c2e84293 --- /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 + +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 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 + + // 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 00000000000..9d0bcbada55 --- /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 +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 << ""; + + CurrentFile = &FileDesc->getSourceText(); + + std::cout << " at " << CurrentFile->getFilename() << ":" << LineNo; + if (ColNo) std::cout << ":" << ColNo << "\n"; + } + + if (printSourceLine(LineNo)) + std::cout << "\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 &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_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 &SourceFiles = + getProgramInfo().getSourceFiles(); + std::cout << "Source files for the program:\n"; + for (std::map::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 << "\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 &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::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::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"; + } +} diff --git a/tools/llvm-db/Makefile b/tools/llvm-db/Makefile new file mode 100644 index 00000000000..e261f81c60c --- /dev/null +++ b/tools/llvm-db/Makefile @@ -0,0 +1,60 @@ +##===- tools/llvm-db/Makefile ------------------------------*- Makefile -*-===## +# +# 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. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../.. +TOOLNAME = llvm-db + +# Get the $(ARCH) setting +include $(LEVEL)/Makefile.config + +# Generic JIT libraries +JITLIBS = lli-jit codegen executionengine +ARCHLIBS = + +# You can enable the X86 JIT on a non-X86 host by setting the flag +# ENABLE_X86_JIT on the make command line. If not, it will still be +# enabled automagically on an X86 host. +ifeq ($(ARCH), x86) + ENABLE_X86_JIT = 1 +endif + +# What the X86 JIT requires +ifdef ENABLE_X86_JIT + CPPFLAGS += -DENABLE_X86_JIT + JITLIBS += x86 selectiondag + # X86 doesn't require any ARCHLIBS +endif + +# You can enable the Sparc JIT on a non-Sparc host by setting the flag +# ENABLE_SPARC_JIT on the make command line. If not, it will still be +# enabled automagically on an Sparc host. +ifeq ($(ARCH), Sparc) + ENABLE_SPARC_JIT = 1 +endif + +# What the Sparc JIT requires +ifdef ENABLE_SPARC_JIT + CPPFLAGS += -DENABLE_SPARC_JIT + JITLIBS += sparc + ARCHLIBS += sched livevar instrument.a profpaths \ + bcwriter transforms.a ipo.a ipa.a datastructure.a regalloc \ + select +endif + +USEDLIBS = lli-interpreter $(JITLIBS) $(ARCHLIBS) scalaropts analysis.a \ + transformutils.a debugger bcreader vmcore support target.a + + +# Have gcc tell the linker to export symbols from the program so that +# dynamically loaded modules can be linked against them. +# +TOOLLINKOPTS = $(PLATFORMLIBDL) + +include $(LEVEL)/Makefile.common + diff --git a/tools/llvm-db/llvm-db.cpp b/tools/llvm-db/llvm-db.cpp new file mode 100644 index 00000000000..124db2ba06a --- /dev/null +++ b/tools/llvm-db/llvm-db.cpp @@ -0,0 +1,88 @@ +//===- llvm-db.cpp - LLVM 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 utility implements a simple text-mode front-end to the LLVM debugger +// library. +// +//===----------------------------------------------------------------------===// + +#include "CLIDebugger.h" +#include "Support/CommandLine.h" +#include + +using namespace llvm; + +namespace { + // Command line options for specifying the program to debug and options to use + cl::opt + InputFile(cl::desc(""), cl::Positional, cl::init("")); + + cl::list + InputArgs("args", cl::Positional, cl::desc(""), + cl::ZeroOrMore); + + // Command line options to control various directory related stuff + cl::list + SourceDirectories("directory", cl::value_desc("directory"), + cl::desc("Add directory to the search for source files")); + cl::alias SDA("d", cl::desc("Alias for --directory"), + cl::aliasopt(SourceDirectories)); + + cl::opt + WorkingDirectory("cd", cl::desc("Use directory as current working directory"), + cl::value_desc("directory")); + + // Command line options specific to the llvm-db debugger driver + cl::opt Version("version", cl::desc("Print version number and quit")); + cl::opt Quiet("quiet", cl::desc("Do not print introductory messages")); + cl::alias QA1("silent", cl::desc("Alias for -quiet"), cl::aliasopt(Quiet)); + cl::alias QA2("q", cl::desc("Alias for -quiet"), cl::aliasopt(Quiet)); +} + +//===----------------------------------------------------------------------===// +// main Driver function +// +int main(int argc, char **argv, char * const *envp) { + cl::ParseCommandLineOptions(argc, argv, + " llvm source-level debugger\n"); + + if (Version || !Quiet) { + std::cout << "llvm-db: The LLVM source-level debugger\n"; + if (Version) return 1; + } + + // Merge Inputfile and InputArgs into the InputArgs list... + if (!InputFile.empty() && InputArgs.empty()) + InputArgs.push_back(InputFile); + + // Create the CLI debugger... + CLIDebugger D; + + // Initialize the debugger with the command line options we read... + Debugger &Dbg = D.getDebugger(); + + // Initialize the debugger environment. + Dbg.initializeEnvironment(envp); + Dbg.setWorkingDirectory(WorkingDirectory); + for (unsigned i = 0, e = SourceDirectories.size(); i != e; ++i) + D.addSourceDirectory(SourceDirectories[i]); + + if (!InputArgs.empty()) { + try { + D.fileCommand(InputArgs[0]); + } catch (const std::string &Error) { + std::cout << "Error: " << Error << "\n"; + } + + Dbg.setProgramArguments(InputArgs.begin()+1, InputArgs.end()); + } + + // Now that we have initialized the debugger, run it. + return D.run(); +} -- 2.34.1